【问题标题】:Reading image into JS to subsequently compress it将图像读入 JS 以随后对其进行压缩
【发布时间】:2018-01-31 07:06:18
【问题描述】:

我是一名服务器端开发人员,刚接触前端开发。我一直在修改 vanilla Javascript,需要澄清一些概念。

我正在试验的用例是通过 JS 处理图像上传(以及在所述上传到服务器之前由空中 JS 促进的压缩)。

目前我被困在第一步。想象一下以下简单的设置:

<form method="POST" action="some_url">
<input type="file" id="browse_image" onchange="compressImage(event)">
<input type="submit" value="Submit">
</form>

我的问题是:

我在哪一步尝试将图像传递给 JS 函数(假设我的目标是压缩它并将其发送到服务器)?这会发生在图像选择时(即按下browse 按钮)还是在按下Submit 时?我在哪里举办活动以及如何从那里开始?一个带有示例的快速说明性答案会很棒!


我一直在尝试在图像选择时这样做(无济于事):

function compressImage(e){

     var reader = new FileReader();
     reader.readAsDataURL(e.target.files[0]);
     console.log(reader);
}

如果能获得一个概念性的演练以及一个快速的说明性示例,那就太好了。仅限 Vanilla JS,在掌握 JS 的窍门之前,我不会接触 JQuery。

【问题讨论】:

  • 您的input type="submit" 中有一个轻微的错字(需要在value="Submit 之后加上引号),但除此之外,给定的代码对我来说就很好。你有什么具体问题?
  • @Hamms:很好。好吧,console.log 声明喷出FileReader { readyState: 1, result: null, error: null, onloadstart: null, onprogress: null, onload: null, onabort: null, onerror: null, onloadend: null }。这告诉我什么都没有被捕获。对于下一步该做什么,我有两个(或三个)想法,因此是个问题。

标签: javascript


【解决方案1】:

在我看来(但这有点主观),你会在两个地方都这样做。

  1. 用户从您的输入中选择一个文件
  2. 你通过js处理文件
  3. 如果您的处理失败(例如文件不是图像/损坏/谁知道),您可以直接让用户知道。
  4. 如果处理成功,当用户单击提交时,您会覆盖表单的默认行为,并发送包含新文件/Blob 的FormData,而不是原始文件。

var toSend = null, // here we will store our processed File/Blob (the one to send)
browse_image = document.getElementById('browse_image');

// when user selects a new File
browse_image.onchange = process_user_file;
// when user decides to send it to server
document.querySelector('form').onsubmit = overwrite_default_submit;

// grab the file from the input and process it
function process_user_file(evt) {
  // since we attached the event listener through elem.onevent,
  // 'this' refers to the input
  var file = this.files[0];
  // here do your compression, for demo, we'll just check it's a png file
  var reader = new FileReader();
  // a FileReader is async, so we pass the actual checking script as the onload handler
  reader.onload = checkMagicNumber;
  reader.readAsArrayBuffer(file.slice(0,4));
}

// we don't want the real form to be submitted, but our processed File/Blob
function overwrite_default_submit(evt) {
  // block the form's submit default behavior
  evt.preventDefault();
  // create a new form result from scratch
  var form = new FormData();
  // add our File/Blob
  form.append("myfile", toSend, browse_image.files[0].name);
  // create a new AJAX request that will do the same as original form's behavior
  var xhr = new XMLHttpRequest();
  xhr.open('POST', evt.target.action);
//  xhr.send(form); // uncomment to really send the request
  console.log('sent', toSend);
}

// simply checks if it's really a png file
// for you, it will be your own compression code,
//  which implementation can not be discussed in this answer
function checkMagicNumber(evt) {
  var PNG = '89504e47';
  var arr = new Uint8Array(evt.target.result);
  var header = "";
  for(var i = 0; i < arr.length; i++) {
     header += arr[i].toString(16);
  }
  // some user friendly actions
  if(header !== PNG) {
    alert('not a png file'); // let the user know it didn't work
    browse_image.value = ""; // remove invalid File
    sub.disabled = true; // avoid the form's submission
    toSend = null; // nothing to send
  }
  else {
    toSend = browse_image.files[0]; // for demo we don't actually modify it...
    sub.disabled = false; // allow form's submission
  }
}
<form method="POST" action="some_url">
  <label>Please select a .png file</label>
  <input type="file" id="browse_image" name="myfile">
  <input type="submit" value="Submit" disabled id="sub">
</form>

Ps:请注意,即使您的原始代码也不会发送任何内容,因为您的表单中没有任何输入具有 name 属性。

【讨论】:

  • @HassanBaig 好吧,这取决于您决定何时执行此操作,但这允许例如事先告知您所获得的内容是否正确,以及 IMM 以获得更好的用户体验,最好让用户了解尽快,因此,尽快进行处理。但是有些人可能不同意,因为这种方法的问题是您的用户很可能在他们第一次选择文件和点击提交之间改变主意,在这种情况下,您会无用地处理一些文件。所以说真的,这是一个意见问题。
  • 更好的方法是在用户选择时进行默认检查,只有在用户决定发送时才进行真正的处理。唯一真正需要的操作是覆盖表单的默认行为。
  • @HassanBaig 我在代码中添加了一些 cmets,希望这能帮助您更好地理解那里发生的事情。对于this.files 问题,您确定您像我一样添加了事件处理程序吗?要获取图像的naturalWidthnaturalHeight,您必须自己从文件数据中获取它(困难但快速的方法),或者使用&lt;img&gt; 元素(简单但缓慢的方法)。但这远远超出了这个问题的范围,has already been answered.
  • @HassanBaig 它更慢,因为使用&lt;img&gt; 方法,浏览器不仅会解析图像的元数据,还会解包其内容,应用必要的 ColorProfile(如果有),甚至实际渲染某些浏览器(例如chrome)中的图像,并且还将未压缩的版本存储在内存中,以便下次可以更快地渲染。而当我们自己解析它时,我们只请求文件,然后,在最好的情况下,我们只需要读取 20 个字节的数据。内存中没有任何内容,I/O 影响很小。
  • CheckMagicNumber 是一个事件处理程序,只有在 readAsArrayBuffer 异步代码完成时才会执行。 readAsArrayBuffer 在我的版本中读取的是文件的 4 字节切片,而在其他版本中,它读取整个文件。
猜你喜欢
  • 2015-05-05
  • 1970-01-01
  • 2017-03-15
  • 2017-05-17
  • 2016-10-02
  • 1970-01-01
  • 2022-01-18
  • 2016-04-28
  • 1970-01-01
相关资源
最近更新 更多