【问题标题】:How to make the following readAsDataURL return multiple readAsDataURLs?如何使以下 readAsDataURL 返回多个 readAsDataURL?
【发布时间】:2017-06-13 02:44:50
【问题描述】:

此函数读取从输入字段获取的文件并返回其 dataUrls:

readAsDataURL (target) {
  // target => <input type="file" id="file">
  var reader = new FileReader()
  return new Promise(function (resolve, reject) {
    reader.onload = function (event) {
      resolve(event.target.result)
    }
    reader.readAsDataURL(target.files[0])
  })
},

如您所见,该函数仅处理单个文件。我想返回多个文件。我试着做reader.readAsDataURL(target.files),但得到了这个错误:'readAsDataURL' on 'FileReader': parameter 1 is not of type 'Blob'.

如何让函数返回多个dataUrls?

【问题讨论】:

  • 请注意,如果您尝试通过例如&lt;img&gt;&lt;video&gt; 将多个媒体文件显示到文档中,那么甚至不要采用FileReader 方式:URL.createObjectURL()是同步的,不需要时间,不需要内存,并且应该总是优先于大量的 dataURIs
  • @Kaiido 哦,我不知道这个。你能用示例代码写一个答案吗?

标签: javascript file


【解决方案1】:

您是否尝试过使用Promise.all?您也许可以执行以下操作:

fileToDataURL(file) {
  var reader = new FileReader()
  return new Promise(function (resolve, reject) {
    reader.onload = function (event) {
      resolve(event.target.result)
    }
    reader.readAsDataURL(file)
  })
}  

readAsDataURL (target) {
  // target => <input type="file" id="file">
  var filesArray = Array.prototype.slice.call(target.files)      
  return Promise.all(filesArray.map(fileToDataURL))
}

这基本上与您设置的相同,但为每个承诺创建一个承诺,仅在所有完成后将结果返回到数组中。


忘记了不能直接把target.files当成数组,所以需要先转换。为此,您可以使用:Array.prototype.slice.call(target.files)

【讨论】:

  • 使用Promise.all的正确方法,但FileList对象上没有map方法
  • Kaiido 是对的:TypeError: target.files.map is not a function 那么正确的方法是什么?
  • 已更新我的答案,包括将文件转换为正确的数组
  • @MattWay 谢谢。它也可以这样工作:return Promise.all([...target.files].map(utils.fileToDataURL))(出于某种神奇的原因。)
  • 你是对的。扩展运算符本质上与我使用的切片调用做同样的事情。我会使用你的方法(只是忘了它)。
【解决方案2】:

从问题中不清楚您将如何处理这些 dataURI,但大多数时候,您实际上并不需要它。

为了能够将来自用户文件或 Blob 的媒体显示到您的文档中,最好使用 URL.createObjectURL() 方法,该方法将返回一个 blobURI,您可以像使用任何 url 一样使用它。

blobURIs 优势的非详尽列表:

  • 是同步的。你不需要你的 Promise 包装事件处理程序,所有这些都可以在一个循环中完成。
  • 速度很快。浏览器不会读取传递的文件或 Blob,它只是在内存中创建指向 HDD 中真实文件的符号链接。
  • 内存轻量级。因为前一点。与readAsDataURL() 相比,您应该注意后者将首先完整读取文件,将其转换为 base64 字符串(比原始数据大约 34%),最后,您会将这个大字符串存储在 DOM 的标记中,让它永远存在于记忆中。从那时起,浏览器将再次读取它,将其转换为二进制,再次将其读取为二进制,最后将其作为普通资源处理。

“但是我需要发送到服务器!”

然后直接发送文件,作为多部分数据。前端的工作更少,管道中的工作更少(记住大约 34%),如果你想在那里保存文件,服务器端的工作也更少。 大多数服务器端语言都有简单的内置方法来从 POST 请求中捕获文件,您可以使用 FormData API 从前面轻松发送它。


这是您的代码示例,使用 blobURI,它要求所有文件都是图像。

document.querySelector('input').onchange = function(){
  // FileList doesn't have an forEach method yet
  Array.prototype.forEach.call(this.files, function(file){
    if(file.type.indexOf('image/' === 0)){
      var i = new Image();
      i.src = URL.createObjectURL(file);  // creates a blobURI
      document.body.appendChild(i);
      }
    });
  };
&lt;input type="file" multiple&gt;

但请注意,这种方法也可能有缺点

  • 不要将它与 MediaStreams 一起使用。它将锁定 MediaStream 的源,直到 blobURI 被撤销(在调用 URL.revokeObject(blobURI) 或硬刷新页面时。在这种情况下,您应该改用 mediaElement.srcObject = MediaStream;
  • 对于您创建的 Blob(不是来自 &lt;input&gt;),同样适用:该 Blob 将被卡在内存中,直到明确释放。在这种情况下,只需在加载资源后调用URL.revokeObjectURL(blobURI)

    img.onload = URL.revokeObjectURL(img.src); 
    img.src = URL.createObjectURL(the_Blob_I_built);
    

【讨论】:

  • 我爱你:* !!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-09
  • 2017-11-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多