Как перейти от Blob к ArrayBuffer

111

Я изучал Blob-объекты и заметил, что когда у вас есть ArrayBuffer, вы можете легко преобразовать его в Blob следующим образом:

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

У меня сейчас вопрос: можно ли перейти от Blob к ArrayBuffer?

Жанлука Скальери
источник
2
Большие двоичные объекты не в собственном формате JS. Они похожи на ссылки на данные, но не на сами данные. Данные из a Blobнельзя прочитать напрямую, но это можно сделать с помощью некоторых API.
tripulse

Ответы:

39

ResponseAPI потребляет (неизменное) , 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 несовместим со старыми ( древними ) браузерами, поэтому на всякий случай ознакомьтесь с таблицей совместимости браузеров ;)

трипульс
источник
1
Это должен быть главный ответ ИМО.
Кэмерон Мартин
const arrayBuffer = ждать нового ответа (blob) .arrayBuffer ();
R.Cha
1
@ R.Cha Спасибо за исправление. Я этого не заметил!
tripulse
2
Умный трюк, не путать с Blob.arrayBuffer()которым на самом деле имеет довольно плохую совместимость даже в 2020 году, caniuse.com/#feat=mdn-api_blob_arraybuffer или developer.mozilla.org/en-US/docs/Web/API/Blob/arrayBuffer
jpschroeder
Какая у этого производительность? Копирует ли он данные в Blob или просто возвращает их представление?
GaryO
141

Вы можете использовать FileReaderдля чтения Blobкак 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://stackoverflow.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: Спасибо Клаусу Кляйну за совет о event.target.resultсравненииthis.result

Ссылка:

картофельный салат
источник
24
Разве это не похоже на большой объем кода ... для чего-то, что должно быть простым?
Хенли Чиу
3
@HenleyChiu Я отредактировал ответ, включив в него короткую версию кода. Более длинный пример предназначен быть полностью автономным (показывает, как создать ArrayBuffer, Blob и обратно). Мне не удалось найти синхронный способ чтения Blob без использования Web Worker и FileReaderSync .
potatosalad
1
Некоторые люди действительно хотят доказать, что ад обратных вызовов существует. Это имеет смысл для БОЛЬШИХ BLOB-объектов, но для обычных случаев использования JavaScript должен предоставлять метод синхронизации.
lama12345
это было очень полезно
Джимми Обоньо Абор
@Anuj Ты ошибся. Это не должно относиться к this. <EventTarget>.resultдолжен это исправить!
tripulse
17

Просто чтобы дополнить ответ г-на @potatosalad.

Вы на самом деле не нужно , чтобы получить доступ к функции сферы , чтобы получить результат на OnLoad обратного вызова, вы можете свободно сделать следующее на событие параметра:

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);
Клаус Кляйн
источник
15

Или вы можете использовать API выборки

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

Я не знаю, в чем разница в производительности, и это также будет отображаться на вкладке вашей сети в DevTools.

Арлен Бейлер
источник
17
Или простоnew Response(blob).arrayBuffer()
Endless
5

Существует в настоящее время (& FF 69+ хром 76+) а Blob.prototype.arrayBuffer () метод , который будет возвращать Promise решения с ArrayBuffer , представляющей данные сгустка в.

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

Кайидо
источник
2
К сожалению, в настоящее время не работает в Safari (пока)
HerrZatacke
@HerrZatacke, см. Мой ответ
Pacerier
0

await blob.arrayBuffer() это хорошо.

Проблема в том, что нужна поддержка iOS / Safari .. для этого понадобится следующее :

if(!new Blob().arrayBuffer){
  Blob.prototype.arrayBuffer =function(){ return new Response(this).arrayBuffer() }
}
Pacerier
источник
stackoverflow.com/a/51758200/3702797 и все остальные резервные варианты уже есть.
Kaiido