ArrayBuffer разом з об’єктами представлень, як частина JavaScript, описані в ECMA стандарті.
Також в браузерах, специфікацією File API, визначено додаткові високорівневі об’єкти для роботи з даними, зокрема Blob.
Blob складається з необов’язкового рядку type (зазвичай MIME тип) та blobParts – послідовності інших Blob об’єктів, рядків та BufferSource.
Приклад синтаксису конструктору:
new Blob(blobParts, options);- blobPartsмасив, що може містити значення типу- Blob/- BufferSource/- String.
- optionsнеобов’язковий об’єкт з властивостями:- type– рядок, що дозволяє додати тип для- Blob, переважно використовуються MIME тип, наприклад,- image/png,
- endings– визначає чи потрібно привести всі символи нового рядку, при створенні- Blob, у відповідність до формату поточної операційної система (- \r\nабо- \n). Типове значення- "transparent"(не вносити змін), але якщо потрібно внести зміни, то необхідно вказати- "native".
 
Наприклад:
// створення Blob з рядку
let blob = new Blob(["<html>…</html>"], {type: 'text/html'});
// зверніть увагу: першим аргументом повинен бути масив [...]// створення Blob з типізованого масиву і рядку
let hello = new Uint8Array([72, 101, 108, 108, 111]); // "Hello" у бінарному форматі
let blob = new Blob([hello, ' ', 'world'], {type: 'text/plain'});Отримати зріз з Blob можна наступним чином:
blob.slice([byteStart], [byteEnd], [contentType]);- byteStart– початковий байт, типове значення 0.
- byteEnd– останній байт (не включно, типово до кінця).
- contentType– визначає- typeнового об’єкту, типове значення буде таким же, як у початкових даних.
Аргументи такі ж самі, як у array.slice, від’ємні числа теж можна використовувати.
Blob об’єкти незмінніДані в Blob не можуть бути зміненими, але ми можемо зробити зріз з їх частини, створити новий Blob з них, змішати їх в новий Blob і так далі.
Поведінка відповідає рядкам в JavaScript: ми не можемо змінити якийсь символ в рядку, але ми можемо створити новий.
Blob як URL
Blob можна використати як URL для показу вмісту HTML тегів <a>, <img> та інших.
Blob об’єкти можна легко завантажити/вивантажити, під час запиту в заголовку Content-Type буде використано значення поля type.
Розгляньмо простий приклад. Після кліку на посилання, ви завантажите динамічно згенерований Blob, як файл з вмістом hello world:
<!-- атрибут download змішує браузер завантажити вміст замість відкриття сторінки -->
<a download="hello.txt" href='#' id="link">Download</a>
<script>
let blob = new Blob(["Hello, world!"], {type: 'text/plain'});
link.href = URL.createObjectURL(blob);
</script>Посилання також можна створити динамічно в JavaScript та імітувати клік за допомогою link.click(), після чого завантаження розпочнеться автоматично.
Ось приклад коду, що дозволяє користувачу завантажити динамічно створений Blob без HTML:
let link = document.createElement('a');
link.download = 'hello.txt';
let blob = new Blob(['Hello, world!'], {type: 'text/plain'});
link.href = URL.createObjectURL(blob);
link.click();
URL.revokeObjectURL(link.href);URL.createObjectURL отримує аргументом Blob та створює унікальний URL для нього у форматі blob:<origin>/<uuid>.
Ось як виглядає значення link.href:
blob:https://javascript.info/1e67e00e-860d-40a5-89ae-6ab0cbee6273Для кожного URL, що створено за допомогою URL.createObjectURL, браузер зберігає відображення URL → Blob. Тому такі URL короткі, але дозволяють отримати доступ до Blob.
Згенерований URL, разом з посиланням на нього, існує тільки всередині поточної сторінки, доки вона відкрита. Це дозволяє посилатися на Blob в тегах <img>, <a> або будь-якому об’єкті, що очікує URL.
Але це спричиняє побічний ефект. Доки існує посилання на Blob у відображенні, Blob повинен залишатися в пам’яті. Браузер не може вивільнити пам’ять, що зайнята Blob.
Відображення автоматично очищується, коли сторінка закривається. Тому, якщо потрібно, щоб застосунок довго був активний – очищення пам’яті може трапитися нескоро.
Тому після створення URL Blob буде залишатися в пам’яті навіть, коли вже не потрібен.
URL.revokeObjectURL(url) видаляє посилання у внутрішньому відображенні, що дозволяє видалити Blob (якщо немає інших посилань на нього) і звільнити пам’ять.
В останньому прикладі, ми цілеспрямовано одразу викликаємо URL.revokeObjectURL(link.href), бо очікуємо, що Blob буде завантажено один раз.
У попередньому прикладі, з HTML посиланням, на яке можна клікнути, ми не викликаємо URL.revokeObjectURL(link.href), бо це зробить Blob не доступним. Після видалення посилання на Blob з відображення URL більше не спрацює.
Blob в base64
Іншим способом отримати доступ до Blob, замість URL.createObjectURL, є перетворення Blob в base64 закодований рядок.
Це кодування дозволяє представити дані як рядок ASCII символів від 0 до 64. І, що найважливіше, ми можемо використовувати це кодування в “data-urls”.
Data url має формат data:[<mediatype>][;base64],<data>. Ми можемо використовувати такі посилання будь-де, як і “звичайні”.
Наприклад, смайлик:
<img src="data:image/png;base64,R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMlWLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mAbiwWtuf0uxFAgA7">Браузер розкодує рядок та покаже зображення: 
Для перетворення Blob в base64 ми будемо використовувати вбудований об’єкт FileReader. Він може читати дані з Blob в різних форматах. В наступному розділі next chapter ми глибше з ним познайомимось.
Демонстрація завантаження Blob за допомогою base64:
let link = document.createElement('a');
link.download = 'hello.txt';
let blob = new Blob(['Hello, world!'], {type: 'text/plain'});
let reader = new FileReader();
reader.readAsDataURL(blob); // перетворить Blob в base64 та викличе onload
reader.onload = function() {
  link.href = reader.result; // data url
  link.click();
};Обидва способи створення URL з Blob доступні для використання. Але, переважно, URL.createObjectURL(blob) простіше та швидше.
- Необхідно видаляти посилання на об’єкт для звільнення пам’яті.
- Безпосередній доступ до Blobбез проміжного “закодування/розкодування”.
- Немає потреби звільняти посилання на об’єкти.
- Втрати швидкодії та навантаження на пам’ять у разі кодування великих Blobоб’єктів.
Зображення в Blob
Також ми можемо створити Blob із зображення, його частини чи навіть зі скріншоту сторінки. Це стане у нагоді, якщо кудись потрібно завантажити світлину.
Робота з зображеннями відбувається за допомогою елементу <canvas>:
- canvas.drawImage використовується для показу зображення.
- Виклик canvas методу .toBlob(callback, format, quality) створює Blobта виконуєcallback, після закінчення.
В наступному прикладі, зображення тільки копіюється, але, за потреби, ми можемо обрізати чи трансформувати його перед створенням Blob:
// беремо будь-яке зображення
let img = document.querySelector('img');
// створюємо <canvas> такого ж розміру
let canvas = document.createElement('canvas');
canvas.width = img.clientWidth;
canvas.height = img.clientHeight;
let context = canvas.getContext('2d');
// копіюємо в нього зображення (цей метод дозволяє вирізати частину зображення)
context.drawImage(img, 0, 0);
// наприклад, можна викликати context.rotate() або багато інших операцій
// toBlob -- це асинхронна операція, передану функцію буде викликано після готовності
canvas.toBlob(function(blob) {
  // Blob готовий, завантажуємо його
  let link = document.createElement('a');
  link.download = 'example.png';
  link.href = URL.createObjectURL(blob);
  link.click();
  // видаляємо внутрішнє посилання на Blob, щоб браузер міг звільнити пам’ять
  URL.revokeObjectURL(link.href);
}, 'image/png');Якщо ви надаєте перевагу async/await синтаксису замість функцій зворотнього виклику:
let blob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png'));Для створення знімків екрану можна використовувати бібліотеку https://github.com/niklasvh/html2canvas. Вона просто обходить сторінку і малює її в <canvas>. Потім ми можемо отримати Blob як показано вище.
Blob в ArrayBuffer
Конструктор Blob дозволяє створювати Blob майже з будь-чого, тим паче з BufferSource.
Але якщо нам потрібно виконати низькорівневу обробку, ми можемо отримати найнижчий рівень ArrayBuffer з blob.arrayBuffer():
// отримати arrayBuffer з blob
const bufferPromise = await blob.arrayBuffer();
// or
blob.arrayBuffer().then(buffer => /* process the ArrayBuffer */);Від Blob до потоку
Коли ми читаємо та пишемо в blob дані розміром понад 2 ГБ, використання arrayBuffer стає більш інтенсивним для нас. На цьому етапі ми можемо безпосередньо перетворити blob на потік.
Потік – це спеціальний об’єкт, який дозволяє читати з нього (або записувати в нього) частина за частиною. Це виходить за рамки цієї статті, але ось приклад, і ви можете прочитати більше на сторінці https://developer.mozilla.org/en-US/docs/Web/API/Streams_API. Потоки зручні для даних, які придатні для обробки частинами.
Метод stream() інтерфейсу Blob повертає ReadableStream, який під час читання повертає дані, що містяться в Blob.
Прочитати з нього ми можемо, наприклад, ось так:
// отримати readableStream з blob
const readableStream = blob.stream();
const stream = readableStream.getReader();
while (true) {
  // для кожної ітерації: дані є наступним фрагментом(частиною) blob
  let { done, value } = await stream.read();
  if (done) {
    // більше немає даних у потоці
    console.log('all blob processed.');
    break;
  }
   // зробити щось із частиною даних, яку ми щойно прочитали з blob
  console.log(value);
}Підсумки
Якщо ArrayBuffer, Uint8Array та інші BufferSource представляють просто “бінарні дані”, то Blob є “типізованими бінарними даними”.
Це робить Blob зручним для вивантаження/завантаження, що часто потрібно робити в браузері.
Методи, що виконують запити, як-от XMLHttpRequest, fetch та інші, можуть безпосередньо працювати з Blob, як і з іншими типами бінарних даних.
Ми можемо легко трансформувати дані між Blob та іншими низькорівневими бінарними типами:
- Ми можемо створити Blobз типізованих масивів з використанням конструкторуnew Blob(...).
- Ми можемо повернути ArrayBufferз Blob за допомогоюblob.arrayBuffer(), а потім створити над ним подання для низькорівневої двійкової обробки.
Потоки перетворення дуже корисні, коли нам потрібно обробляти великий blob. Ви можете легко створити ReadableStream з blob. Метод stream() інтерфейсу Blob повертає ReadableStream, який після читання повертає дані, що містяться у цьому blob.
Коментарі
<code>, для кількох рядків – обгорніть їх тегом<pre>, для понад 10 рядків – використовуйте пісочницю (plnkr, jsbin, codepen…)