- Операторы
- Управляющие инструкции
- JS Объекты
- браузер BOM
- HTML DOM
- События
- HTML Объекты
- Промисы, async/await
- Сетевые запросы
- Бинарные данные и файлы
- ArrayBuffer, бинарные массивы
- TextDecoder и TextEncoder
- Blob
- File и FileReader
- Разное
Blob
Объект Blob представляет из себя подобный файлу объект с неизменяемыми, необработанными данными; они могут читаться как текст или двоичные данные.
Blob-ы могут представлять данные, которые не обязательно должны быть в родном для JavaScript формате. Интерфейс File
основан на Blob
, наследует функциональность Blob
и расширяет его для поддержки файлов на стороне пользователя.
Объект Blob состоит из необязательной строки type
(обычно MIME-тип) и blobParts – последовательности других объектов Blob, строк и BufferSource.
Благодаря type мы можем загружать и скачивать Blob-объекты, где type
естественно становится Content-Type
в сетевых запросах.
Конструктор
Конструктор имеет следующий синтаксис:
new Blob( array, options );
- array
- массив
Array
из объектовArrayBuffer
,ArrayBufferView
,Blob
,DOMString
, или смесь любых из подобных объектов, которая может быть размещена внутри Blob.DOMStrings
представлены в кодировке UTF-8. - options
- необязательный объект с дополнительными настройками:
type
– тип объекта, обычно MIME-тип, например. image/png,endings
– если указан, то окончания строк создаваемого Blob будут изменены в соответствии с текущей операционной системой (\r\n или \n). По умолчанию "transparent
" (ничего не делать), но также может быть "native
" (изменять).
Например:
// создадим Blob из строки let blob = new Blob(["…"], {type: 'text/html'}); // обратите внимание: первый аргумент должен быть массивом [...]
// создадим Blob из типизированного массива и строк let hello = new Uint8Array([72, 101, 108, 108, 111]); // "hello" в бинарной форме let blob = new Blob([hello, ' ', 'world'], {type: 'text/plain'});
Свойства
- Blob.isClosed
- Логическое значение, показывающее, вызывался ли метод
Blob.close()
у blob. Закрытый blob не может быть прочитан. - Blob.size
- Размер данных, содержащихся в объекте
Blob
, в байтах. - Blob.type
- Строка с MIME-типом данных, содержащихся в
Blob
. Если тип неизвестен, строка пуста.
Методы
- Blob.close()
- Закрывает Blob объект, по возможности освобождая занятые им ресурсы.
- Blob.stream()
- Возвращает
ReadableStream
, который может использоваться для чтения содержимогоBlob
. - Blob.text()
- Возвращает promise, с его содержимым в виде строки, интерпретируемое как текст UTF-8.
- Blob.slice()
- Возвращает новый
Blob
объект, содержащий данные в указанном диапазоне байтов исходногоBlob
.
Чтобы получить срез Blob, используется:
blob.slice( [byteStart], [byteEnd], [contentType] );
- byteStart
- стартовая позиция байта, по умолчанию 0.
- byteEnd
- последний байт, по умолчанию до конца.
- contentType
- тип type создаваемого Blob-объекта, по умолчанию такой же, как и исходный.
Аргументы – как в array.slice, отрицательные числа также разрешены.
Blob не изменяем (immutable)
Нельзя изменять данные напрямую в Blob, но можно делать срезы и создавать новый Blob на их основе, объединять несколько объектов в новый и так далее.
Это поведение аналогично JavaScript-строке: нельзя изменить символы в строке, но можно создать новую исправленную строку на базе имеющейся.
Blob как URL
Blob может быть использован как URL для <a>
, <img>
или других тегов, для показа содержимого.
Простой пример: При клике на ссылку загружается динамически генерируемый Blob с hello world
содержимым как файл:
<!-- download атрибут указывает браузеру делать загрузку вместо навигации --> <a download="hello.txt" href='#' id="link">Загрузить</a> <script> let blob = new Blob(["Hello, world!"], {type: 'text/plain'}); link.href = URL.createObjectURL(blob); </script>
Можно создать ссылку динамически, используя только JavaScript, и эмулировать на ней клик, используя link.click()
, тогда загрузка начнётся автоматически:
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>
.
Вот как выглядит сгенерированный URL:
blob:https://javascript.info/1e67e00e-860d-40a5-89ae-6ab0cbee6273
Браузер для каждого URL, сгенерированного через URL.createObjectURL
, сохраняет внутреннее соответствие URL → Blob
. Таким образом, такие URL короткие, но дают доступ к большому объекту Blob
.
Сгенерированный url действителен, только пока текущий документ открыт. Это позволяет ссылаться на сгенерированный в нём Blob в <img>
, <a>
или в любом другом объекте, где ожидается url в качестве одного из параметров.
В данном случае возможен побочный эффект. Пока в карте соответствия существует ссылка на Blob, он находится в памяти. Браузер не может освободить память, занятую Blob-объектом.
Ссылка в карте соответствия автоматически удаляется при выгрузке документа, после этого также освобождается память. Но если приложение имеет длительный жизненный цикл, это может произойти не скоро. Таким образом, если мы создадим URL для Blob, он будет висеть в памяти, даже если в нём нет больше необходимости.
URL.revokeObjectURL(url)
удаляет внутреннюю ссылку на объект, что позволяет удалить его (если нет другой ссылки) сборщику мусора, и память будет освобождена.
В последнем примере мы использовали Blob только единожды, для мгновенной загрузки, после мы сразу же вызвали URL.revokeObjectURL(link.href)
.
В предыдущем примере с кликабельной HTML-ссылкой мы не вызывали URL.revokeObjectURL(link.href)
, потому что это сделало бы ссылку недействительной. После удаления внутренней ссылки на Blob
, URL больше не будет работать.
Blob to base64
Альтернатива URL.createObjectURL
– конвертация Blob-объекта в строку с кодировкой base64.
Эта кодировка представляет двоичные данные в виде строки с безопасными для чтения символами в ASCII-кодах от 0 до 64. И что более важно – мы можем использовать эту кодировку для «data-urls».
data url имеет форму data:[<mediatype>][;base64],<data>
. Мы можем использовать такой url где угодно наряду с «обычным» url.
Например, смайлик:
<img src="data:image/png;base64,R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMlWLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mAbiwWtuf0uxFAgA7">
Браузер декодирует строку и показывает смайлик:
Для трансформации Blob в base64 мы будем использовать встроенный в браузер объект типа FileReader
. Он может читать данные из Blob в множестве форматов.
Вот пример загрузки 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; // url с данными link.click(); };
Оба варианта могут быть использованы для создания URL с Blob. Но обычно URL.createObjectURL(blob)
является более быстрым и безопасным.
Изображение в Blob
Мы можем создать Blob для изображения, части изображения или даже создать скриншот страницы. Что удобно для последующей загрузки куда-либо.
Операции с изображениями выполняются через элемент <canvas>
:
- Для отрисовки изображения (или его части) на холсте (canvas) используется canvas.drawImage.
- Вызов canvas-метода .toBlob(callback, format, quality) создаёт Blob и вызывает функцию
callback
при завершении.
В примере ниже изображение просто копируется, но мы можем взять его часть или трансформировать его на canvas перед созданием Blob:
<img src="" alt="" width=200 height=200 /><div id="comm" style="display:none">Картинка сохранена как «example.png»</div> <script> // берём любое изображение let img = document.querySelector('img'); // создаём <canvas> того же размера let canvas = document.createElement('canvas'); canvas.width = img.clientWidth; canvas.height = img.clientHeight; var d=canvas.width; var ctx = canvas.getContext('2d'); ctx.beginPath(); ctx.moveTo(d / 2, 0); ctx.lineTo(d, d); ctx.lineTo(0, d); ctx.closePath(); ctx.fillStyle = 'green'; ctx.fill() // toBlob является асинхронной операцией, для которой callback-функция вызывается при завершении canvas.toBlob(function(blob) { // после того, как Blob создан, загружаем его let link = document.createElement('a'); link.download = 'example.png'; link.href = URL.createObjectURL(blob); img.src=link.href; document.getElementById("comm").style.display="block"; link.click(); // удаляем внутреннюю ссылку на Blob, что позволит браузеру очистить память URL.revokeObjectURL(link.href); }, 'image/png'); </script>
Или если вы предпочитаете 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
из FileReader
:
// получаем arrayBuffer из Blob let fileReader = new FileReader(); fileReader.readAsArrayBuffer(blob); fileReader.onload = function(event) { let arrayBuffer = fileReader.result; };
Итого
В то время как ArrayBuffer
, Uint8Array
и другие BufferSource
являются «бинарными данными», Blob представляет собой «бинарные данные с типом».
Это делает Blob удобным для операций загрузки/выгрузки данных, которые так часто используются в браузере.
Методы, которые выполняют сетевые запросы, такие как XMLHttpRequest и подобные, могут изначально работать с Blob
так же, как и с другими объектами, представляющими двоичные данные.
Мы можем легко конвертировать Blob
в низкоуровневые бинарные типы данных и обратно:
- Мы можем создать Blob из типизированного массива, используя конструктор
new Blob(...)
. - Мы можем обратно создать
ArrayBuffer
из Blob, используяFileReader
, а затем создать его представление для низкоуровневых операций.