Работа с бинарными данными в браузере требует понимания того, как
JavaScript представляет и передаёт низкоуровневые структуры памяти. В
контексте клиентского хранения данных важным становится использование
ArrayBuffer, TypedArray и связанных структур,
поскольку они позволяют сохранять изображения, аудиофрагменты,
криптографические ключи и произвольные байтовые последовательности без
преобразования в строковый формат.
localForage использует единый API поверх различных механизмов хранения (IndexedDB, WebSQL, localStorage), и именно работа со структурированными данными делает возможным сохранение бинарных буферов без ручной сериализации.
ArrayBuffer представляет собой фиксированную область
памяти, содержащую «сырые» байты. Он не интерпретирует данные
самостоятельно, а лишь предоставляет контейнер, к которому можно
подключать представления:
Uint8Array — массив байтов без знакаInt16Array — 16-битные целыеFloat32Array — числа с плавающей точкойDataView — гибкий доступ к байтам с произвольной
интерпретациейКлючевая особенность заключается в том, что ArrayBuffer
не содержит логики преобразования — он лишь хранит последовательность
байтов фиксированной длины.
Внутри localForage работа с бинарными структурами опирается на Structured Clone Algorithm, используемый IndexedDB. Этот механизм позволяет сохранять:
ArrayBufferTypedArrayBlobFileбез необходимости преобразования в JSON или строку.
В отличие от localStorage, где всё приводится к строке,
здесь сохраняется оригинальная структура данных.
При сохранении бинарных данных используется стандартный асинхронный API:
const buffer = new ArrayBuffer(8);
const view = new Uint8Array(buffer);
view[0] = 255;
view[1] = 128;
await localforage.setItem("binary-key", buffer);
В момент вызова происходит передача объекта в IndexedDB, где структура клонируется без потери байтовой информации.
Извлечение данных возвращает тот же тип объекта, который был сохранён:
const result = await localforage.getItem("binary-key");
const view = new Uint8Array(result);
console.log(view[0]);
Тип возвращаемого значения зависит от сохранённого объекта. Если был
сохранён ArrayBuffer, на выходе также будет
ArrayBuffer.
TypedArray часто используется как промежуточное представление перед сохранением:
const pixels = new Uint8Array([10, 20, 30, 40, 50]);
await localforage.setItem("pixels", pixels);
При извлечении данные сохраняют свою форму:
const pixels = await localforage.getItem("pixels");
console.log(pixels instanceof Uint8Array);
Важно учитывать, что при чтении TypedArray может быть восстановлен
как Uint8Array, даже если исходно использовался другой тип
представления.
Blob и ArrayBuffer часто используются
совместно, но имеют различную семантику:
ArrayBuffer — низкоуровневые байтыBlob — неизменяемый объект файла с MIME-типомСохранение Blob:
const blob = new Blob(["hello"], { type: "text/plain" });
await localforage.setItem("file", blob);
При извлечении объект сохраняет файловую структуру, однако при необходимости низкоуровневой обработки часто выполняется преобразование:
const blob = await localforage.getItem("file");
const buffer = await blob.arrayBuffer();
В основе хранения лежит алгоритм структурного клонирования:
В отличие от JSON-сериализации, отсутствует потеря информации, связанная с числами с плавающей точкой, бинарными сегментами или метаданными файла.
Каждая операция записи создаёт независимую копию данных. Изменения
исходного ArrayBuffer после сохранения не влияют на
сохранённое значение.
IndexedDB допускает значительные объёмы данных, однако производительность зависит от:
Не все реализации одинаково восстанавливают сложные типы TypedArray.
В некоторых окружениях восстановление может приводить к упрощённому виду
(Uint8Array вместо специализированного типа).
Иногда требуется преобразование для передачи через API:
function bufferToBase64(buffer) {
let binary = "";
const bytes = new Uint8Array(buffer);
for (let i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
function base64ToBuffer(base64) {
const binary = atob(base64);
const len = binary.length;
const buffer = new ArrayBuffer(len);
const view = new Uint8Array(buffer);
for (let i = 0; i < len; i++) {
view[i] = binary.charCodeAt(i);
}
return buffer;
}
Несмотря на универсальность Base64, его использование увеличивает
размер данных примерно на 33%, тогда как ArrayBuffer
сохраняет компактное бинарное представление.
Бинарные данные часто применяются для локального кэширования изображений:
const response = await fetch("/image.png");
const buffer = await response.arrayBuffer();
await localforage.setItem("image", buffer);
При последующем использовании:
const buffer = await localforage.getItem("image");
const blob = new Blob([buffer], { type: "image/png" });
const url = URL.createObjectURL(blob);
Такой подход исключает необходимость повторной загрузки ресурсов из сети.
Работа с ArrayBuffer в связке с IndexedDB имеет
следующие характеристики:
Оптимизация часто связана с минимизацией количества операций записи и группировкой данных в один буфер.
localForage автоматически выбирает backend:
При отсутствии поддержки бинарного хранения возможна деградация, при которой данные преобразуются в строковые представления, что может приводить к увеличению размера и потере эффективности.
ArrayBuffer часто применяется для хранения:
const key = crypto.getRandomValues(new Uint8Array(32));
await localforage.setItem("key", key.buffer);
Такие данные сохраняют точную байтовую структуру, необходимую для криптографической корректности.
Типичный поток взаимодействия включает этапы:
ArrayBufferBlob или TypedArrayКаждый этап опирается на сохранение неизменной бинарной структуры, что делает возможным построение офлайн-кэша и локальных медиа-хранилищ без промежуточных форматов.