Библиотека localForage предоставляет единый API для работы с
различными типами хранилищ браузера, абстрагируя различия между
IndexedDB, WebSQL и localStorage. Одним из ключевых преимуществ является
возможность хранения бинарных данных, включая Blob,
File и ArrayBuffer, без необходимости ручной
сериализации.
Бинарные типы данных особенно важны при работе с медиа-контентом, файлами и любыми структурами, которые не могут быть корректно представлены в виде строки или JSON.
Blob (Binary Large Object) представляет собой
неизменяемый объект, содержащий сырые данные. Он используется для
хранения изображений, аудиофайлов, видеофрагментов и любых других
бинарных потоков.
В контексте localForage важно учитывать:
Blob поддерживается только в
IndexedDB-драйвереОсновное преимущество — отсутствие необходимости преобразования в base64.
Сохранение бинарных данных осуществляется так же, как и работа с обычными значениями.
import localForage from "localforage";
const imageBlob = new Blob([binaryData], { type: "image/png" });
localForage.setItem("avatar", imageBlob)
.then(() => {
console.log("Blob сохранён");
})
.catch((err) => {
console.error("Ошибка сохранения:", err);
});
Получение данных полностью симметрично операции записи.
localForage.getItem("avatar")
.then((blob) => {
if (blob) {
console.log("Получен Blob:", blob);
} else {
console.log("Данные отсутствуют");
}
})
.catch((err) => {
console.error("Ошибка чтения:", err);
});
Часто Blob используется для отображения изображений или медиа:
localForage.getItem("avatar").then((blob) => {
const url = URL.createObjectURL(blob);
const img = document.createElement("img");
img.src = url;
document.body.appendChild(img);
});
В некоторых сценариях требуется доступ к «сырым» байтам.
function blobToArrayBuffer(blob) {
return blob.arrayBuffer();
}
localForage.getItem("file").then(async (blob) => {
const buffer = await blobToArrayBuffer(blob);
console.log(new Uint8Array(buffer));
});
const buffer = new Uint8Array([1, 2, 3, 4]).buffer;
const blob = new Blob([buffer], { type: "application/octet-stream" });
localForage.setItem("binary", blob);
File является наследником Blob, поэтому
поддерживается аналогично.
const fileInput = document.querySelector("input[type='file']");
fileInput.addEventListener("change", () => {
const file = fileInput.files[0];
localForage.setItem("uploadedFile", file);
});
Чтение:
localForage.getItem("uploadedFile").then((file) => {
console.log(file.name);
console.log(file.type);
console.log(file.size);
});
Типичный сценарий — кэширование аватаров или пользовательских изображений:
fetch("/api/avatar")
.then(res => res.blob())
.then(blob => localForage.setItem("avatar", blob));
Позднее:
localForage.getItem("avatar").then(blob => {
document.querySelector("img").src = URL.createObjectURL(blob);
});
fetch("/audio/track.mp3")
.then(res => res.blob())
.then(blob => localForage.setItem("track", blob));
Воспроизведение:
localForage.getItem("track").then(blob => {
const audio = new Audio(URL.createObjectURL(blob));
audio.play();
});
Blob часто используется для реализации offline-first подхода:
Несмотря на удобство, существуют важные ограничения.
| Драйвер | Поддержка Blob |
|---|---|
| IndexedDB | Полная |
| WebSQL | Частичная |
| localStorage | Нет |
localForage автоматически выбирает IndexedDB при наличии поддержки.
IndexedDB не имеет жесткого лимита, но фактически ограничивается:
Большие Blob могут влиять на:
localForage использует встроенные механизмы IndexedDB, поэтому:
Это критически важно для производительности при работе с большими файлами.
Типичные проблемы при работе с Blob:
QuotaExceededError
Причины:
В редких случаях браузеры могут некорректно обрабатывать нестандартные типы Blob:
new Blob([undefined]) // потенциально проблемный случай
async function saveBlobSafe(key, blob) {
try {
await localForage.setItem(key, blob);
} catch (e) {
console.error("Ошибка записи Blob:", e);
}
}
const blob = await localForage.getItem("image");
const url = URL.createObjectURL(blob);
Важно освобождать память:
URL.revokeObjectURL(url);
Перед сохранением изображений часто применяется:
Для крупных данных иногда эффективнее:
localForage не поддерживает потоковую запись Blob напрямую, поэтому применяется стратегия:
const chunk1 = new Uint8Array([1, 2]);
const chunk2 = new Uint8Array([3, 4]);
const blob = new Blob([chunk1, chunk2]);
await localForage.setItem("file", blob);
localForage может переключаться между драйверами:
В таких случаях:
localForage поддерживает смешанные структуры:
const data = {
name: "image",
file: blob,
created: Date.now()
};
localForage.setItem("record", data);
Однако при вложенных структурах важно учитывать:
async function cacheImage(url) {
const cached = await localForage.getItem(url);
if (cached) return cached;
const blob = await fetch(url).then(r => r.blob());
await localForage.setItem(url, blob);
return blob;
}
async function getMedia(id) {
let media = await localForage.getItem(id);
if (!media) {
media = await fetch(`/media/${id}`).then(r => r.blob());
await localForage.setItem(id, media);
}
return media;
}