如何从 Blob 转到 ArrayBuffer

Posted

技术标签:

【中文标题】如何从 Blob 转到 ArrayBuffer【英文标题】:How to go from Blob to ArrayBuffer 【发布时间】:2013-02-26 19:49:19 【问题描述】:

我正在研究 Blob,我注意到当你有一个 ArrayBuffer 时,你可以很容易地将它转换为 Blob,如下所示:

var dataView = new DataView(arrayBuffer);
var blob = new Blob([dataView],  type: mimeString );

我现在的问题是,是否可以从 Blob 转到 ArrayBuffer?

【问题讨论】:

Blob 不是原生 JS 格式。它们就像对数据的引用,而不是实际数据。来自Blob 的数据无法直接读取,但可以通过一些 API 完成。 【参考方案1】:

您可以使用FileReaderBlob 读取为ArrayBuffer

这是一个简短的例子:

var arrayBuffer;
var fileReader = new FileReader();
fileReader.onload = function(event) 
    arrayBuffer = event.target.result;
;
fileReader.readAsArrayBuffer(blob);

这是一个更长的例子:

// ArrayBuffer -> Blob
var uint8Array  = new Uint8Array([1, 2, 3]);
var arrayBuffer = uint8Array.buffer;
var blob        = new Blob([arrayBuffer]);

// Blob -> ArrayBuffer
var uint8ArrayNew  = null;
var arrayBufferNew = null;
var fileReader     = new FileReader();
fileReader.onload  = function(event) 
    arrayBufferNew = event.target.result;
    uint8ArrayNew  = new Uint8Array(arrayBufferNew);

    // warn if read values are not the same as the original values
    // arrayEqual from: http://***.com/questions/3115982/how-to-check-javascript-array-equals
    function arrayEqual(a, b)  return !(a<b || b<a); ;
    if (arrayBufferNew.byteLength !== arrayBuffer.byteLength) // should be 3
        console.warn("ArrayBuffer byteLength does not match");
    if (arrayEqual(uint8ArrayNew, uint8Array) !== true) // should be [1,2,3]
        console.warn("Uint8Array does not match");
;
fileReader.readAsArrayBuffer(blob);
fileReader.result; // also accessible this way once the blob has been read

这已在 Chrome 27-69、Firefox 20-60 和 Safari 6-11 的控制台中进行了测试。

这里还有一个你可以玩的现场演示:https://jsfiddle.net/potatosalad/FbaM6/

2018-06-23 更新:感谢 Klaus Klein 关于 event.target.resultthis.result 的提示

参考:

https://developer.mozilla.org/en-US/docs/Web/API/FileReader#readAsArrayBuffer() https://www.w3.org/TR/FileAPI/#dfn-readAsArrayBuffer

【讨论】:

这看起来不是很多代码.. 应该很简单吗? @HenleyChiu 我编辑了答案以包含代码的简短版本。较长的示例旨在完全自包含(显示如何创建 ArrayBuffer、Blob 并再次返回)。在不使用 Web Worker 和 FileReaderSync 的情况下,我无法找到一种同步方式来读取 Blob。 有些人真的想证明回调地狱的存在。这对 LARGE blob 有意义,但对于正常用例,JavaScript 应该提供同步方法。 仅供参考 await blob.arrayBuffer() 现在有很好的支持:developer.mozilla.org/en-US/docs/Web/API/Blob/…【参考方案2】:

Response API 使用(不可变的)Blob,可以通过多种方式从中检索数据。 OP 只要求ArrayBuffer,这是它的演示。

var blob = GetABlobSomehow();

// NOTE: you will need to wrap this up in a async block first.
/* Use the await keyword to wait for the Promise to resolve */
await new Response(blob).arrayBuffer();   //=> <ArrayBuffer>

或者你可以使用这个:

new Response(blob).arrayBuffer()
.then(/* <function> */);

注意:API 与旧版(古代)浏览器不兼容,因此请查看Browser Compatibility Table 以启用安全的一面;)

【讨论】:

聪明的技巧,不要与Blob.arrayBuffer()混淆,即使在2020年,caniuse.com/#feat=mdn-api_blob_arraybuffer或developer.mozilla.org/en-US/docs/Web/API/Blob/arrayBuffer实际上兼容性也很差 这个性能怎么样?它是复制 Blob 中的数据还是只返回它的视图? @GaryO 没有对它进行基准测试,我认为它会复制 blob 并返回它的视图。 叹息,它似乎确实复制了。如果 blob 很大,那就不好了! 仅供参考 await blob.arrayBuffer() 现在有很好的支持:developer.mozilla.org/en-US/docs/Web/API/Blob/…【参考方案3】:

或者你可以使用 fetch API

fetch(URL.createObjectURL(myBlob)).then(res => res.arrayBuffer())

我不知道性能差异是什么,这也会显示在 DevTools 的网络选项卡上。

【讨论】:

或者只是new Response(blob).arrayBuffer() await blob.arrayBuffer(),现在有很好的支持:developer.mozilla.org/en-US/docs/Web/API/Blob/…【参考方案4】:

只是为了补充@potatosalad 先生的回答。

您实际上不需要访问函数scope来获取onload回调的结果,您可以在event 参数:

var arrayBuffer;
var fileReader = new FileReader();
fileReader.onload = function(event) 
    arrayBuffer = event.target.result;
;
fileReader.readAsArrayBuffer(blob);

为什么这样更好?因为这样我们就可以在不丢失上下文的情况下使用箭头函数

var fileReader = new FileReader();
fileReader.onload = (event) => 
    this.externalScopeVariable = event.target.result;
;
fileReader.readAsArrayBuffer(blob);

【讨论】:

【参考方案5】:

await blob.arrayBuffer() 不错。

问题是什么时候需要 ios / Safari 支持.. 为此  需要this:

Blob.prototype.arrayBuffer ??=function() return new Response(this).arrayBuffer() 

【讨论】:

***.com/a/51758200/3702797 和所有其他后备都已经存在。 @Kaiido,见***.com/questions/15341912/…。我的方法更优越,在 iOS 支持之前是必需的。 (之后可以简单地删除该行) 而且 IE 中仍然需要 FileReader。我的观点是,您的答案没有提供比先前答案更多的东西。如果一个人想要构建一个 polyfill,那么执行相同任务的所有各种方法都已经存在。 @Kaiido,IE 要求很少见。 ¶ 是的,这是 polyfill 的唯一答案。【参考方案6】:

now (Chrome 76+ & FF 69+) 有一个 Blob.prototype.arrayBuffer() 方法,它会返回一个 Promise 解析,ArrayBuffer 代表 Blob 的数据。

(async () => 
  const blob = new Blob(['hello']);
  const buf = await blob.arrayBuffer();
  console.log( buf.byteLength ); // 5
)();

【讨论】:

很遗憾目前在 Safari 中无法使用(还) @HerrZatacke,看看我的回答 根据developer.mozilla.org/en-US/docs/Web/API/Blob/… Safari 14+ 版本支持此 API。【参考方案7】:

这是一个异步方法,它首先检查arrayBuffer 方法的可用性。此功能向后兼容且面向未来。

async function blobToArrayBuffer(blob) 
    if ('arrayBuffer' in blob) return await blob.arrayBuffer();
    
    return new Promise((resolve, reject) => 
        const reader = new FileReader();
        reader.onload = () => resolve(reader.result);
        reader.onerror = () => reject;
        reader.readAsArrayBuffer(blob);
    );

【讨论】:

以上是关于如何从 Blob 转到 ArrayBuffer的主要内容,如果未能解决你的问题,请参考以下文章

关于blob转到目标库报ORA-01461: 仅能绑定要插入 LONG 列的 LONG 值错误解决方案

如何设置键盘快捷键以跳转到行首/行尾? [关闭]

如何从 HTTP 触发的 Azure 函数返回 blob?

如何从 azure blob 存储下载文件

我们如何使用代理和 NodeJS 从 azure 存储下载 blob?

如何使用 iBatis 从数据库中选择 BLOB 列