Browser Хранилище APIs для Передача файлов Applications
Leverage IndexedDB, File System Access API, и Cache API для файл transfer apps. Хранилище limits, performance, и browser compatibility.
Браузерное хранилище для приложения передачи файлов чётко делится между четырьмя API: IndexedDB — для структурированных метаданных сессий и чанков (транзакционный, асинхронный, типизированный), File System Access API — для чтения и записи многогигабайтных файлов прямо на диск пользователя, Cache API — для HTTP-ответов и ресурсов app-shell, Storage Manager API — для управления квотами и подсказок о персистентности. Origin Private File System (OPFS) находится рядом с ними для изолированного высокопроизводительного ввода-вывода. Выбор правильного API важен, поскольку лимиты варьируются от 1 ГБ в iOS Safari до «60% свободного диска» в настольном Chrome, а неверный выбор рано или поздно приводит к QuotaExceededError в продакшене.
Какой API для какой задачи
Используйте IndexedDB для всего, что выглядит как строка таблицы: сессии загрузки, индексы завершённых чанков, метаданные ссылок, токены отзыва. Асинхронный, транзакционный, индексируемый, переживает закрытие вкладок.
Используйте File System Access API, когда нужно записать байты на диск без загрузки всего файла в RAM — идеально для сохранения расшифрованных загрузок размером более 500 МБ. Chrome, Edge и Opera поддерживают его; Firefox и Safari реализуют лишь read-only подмножество через showOpenFilePicker.
Используйте Cache API для объектов HTTP-ответов — JS-бандл, CSS, иконки и кэшированные ответы API. Он оптимизирован для перехвата fetch в Service Workers.
Используйте OPFS (специальная Origin-Private ветка File System Access API), когда нужно быстрое изолированное, невидимое пользователю хранилище — например, буфер записи при многогигабайтном проходе шифрования. Он достигает дисковой пропускной способности на порядок выше IndexedDB для бинарных блобов.
IndexedDB без острых углов
У IndexedDB печально известный неудобный API, основанный на событиях. Используйте пакет idb Джека Аркибальда (1,5 КБ в gzip) или Dexie.js (20 КБ, более богатый API запросов):
import { openDB } from 'idb';
const db = await openDB('transfers', 2, {
upgrade(db, oldVersion) {
if (oldVersion < 1) {
const sessions = db.createObjectStore('sessions', { keyPath: 'id' });
sessions.createIndex('by_expiry', 'expiresAt');
}
if (oldVersion < 2) {
db.createObjectStore('chunks', { keyPath: ['sessionId', 'index'] });
}
}
});
await db.put('sessions', { id: 'abc', fileName: 'report.pdf', expiresAt: Date.now() + 86400000 });
Миграции версий выполняются в коллбэке upgrade. Всегда защищайте миграции проверкой oldVersion, чтобы пользователи, переходящие с v1 на v3, получили оба шага.
IndexedDB обрабатывает большинство форматов данных, включая Blob и File-ссылки, через structured clone. Это значит, что в записи сессии можно хранить File-хэндл и после перезагрузки вкладки снова прочитать оригинальные байты файла — идеально для возобновляемых загрузок.
File System Access API для больших скачиваний
API позволяет передать записываемый поток в диалог сохранения браузера:
const handle = await window.showSaveFilePicker({
suggestedName: 'decrypted-archive.zip',
types: [{ description: 'Zip', accept: { 'application/zip': ['.zip'] } }]
});
const writable = await handle.createWritable();
await decryptionStream.pipeTo(writable);
Байты идут прямо на диск, не попадая в JS-кучу. Это единственный практичный способ сохранить расшифрованный файл в 10 ГБ в браузере.
Для Firefox и Safari используйте StreamSaver.js как запасной вариант — он использует Service Worker для синтеза потокового ответа, запускающего UI скачивания. Та же эргономика, чуть больше движущихся частей.
Постоянные хэндлы файлов также позволяют приложению переоткрывать файлы между сессиями. После предоставления разрешения через showOpenFilePicker можно сохранить FileSystemFileHandle в IndexedDB и позже вызвать handle.requestPermission() для восстановления доступа без повторного запроса для каждого файла.
OPFS для временного хранилища
Origin Private File System — это изолированное хранилище на уровне источника, которое ведёт себя как файловая система, но невидимо пользователю:
const root = await navigator.storage.getDirectory();
const fh = await root.getFileHandle('scratch.bin', { create: true });
const access = await fh.createSyncAccessHandle(); // только в workers
access.write(buffer, { at: offset });
access.flush();
access.close();
createSyncAccessHandle доступен только внутри Web Workers (включая Service Workers). Синхронный и чрезвычайно быстрый — бенчмарки показывают в 3–10 раз больше, чем IndexedDB для последовательной записи. Используйте его для буферизации нескольких сотен МБ зашифрованного вывода перед загрузкой или для кэширования расшифрованной рабочей копии без засорения папки «Загрузки» пользователя.
Safari 17 выпустил OPFS с синхронными хэндлами доступа; Firefox 111 последовал. Все три основных браузера теперь его поддерживают — это делает его пригодным для продакшена.
Квоты хранилища и как их пережить
Все API делят один пул квот для источника. Приблизительные лимиты:
- Настольный Chrome: 60% свободного диска
- Настольный Firefox: 50% свободного диска, по умолчанию ограничено 2 ГБ на источник
- Настольный Safari: предупреждение при 1 ГБ, растёт до ~20% диска с одобрения пользователя
- iOS Safari: 1 ГБ на источник, активное вытеснение через 7 дней при неиспользовании
- Chrome Android: 10% свободного диска, вытеснение при нехватке памяти
Проверяйте квоты во время выполнения:
const { quota, usage } = await navigator.storage.estimate();
console.log(`Используется ${(usage/1e9).toFixed(2)} ГБ из ${(quota/1e9).toFixed(2)} ГБ`);
Запросите персистентность для критичных хранилищ:
const persisted = await navigator.storage.persist();
Возвращает true, если браузер предоставил постоянное хранилище, означающее, что данные не будут вытеснены под давлением. Chrome предоставляет это автоматически для сайтов, с которыми пользователь активно взаимодействовал; Firefox запрашивает подтверждение.
Cache API для app shell и оффлайн
Cache API хранит пары Request + Response и является правильным выбором внутри Service Workers:
const cache = await caches.open('shell-v7');
await cache.addAll([
'/', '/app.js', '/app.css', '/icons/192.png'
]);
Получение при перехвате:
self.addEventListener('fetch', (e) => {
e.respondWith(caches.match(e.request).then(r => r ?? fetch(e.request)));
});
Не помещайте зашифрованные байты файлов в Cache API. Объект ответа в 2 ГБ за один раз превысит квоту iOS Safari, и после этого его нельзя загрузить диапазоном. Байты принадлежат OPFS или прямо на диск через File System Access.
Корректная обработка вытеснения и потери данных
Неперсистентное хранилище вытесняется — это нужно учитывать. iOS Safari вытесняет через 7 дней неиспользования, независимо от квоты. Chrome вытесняет только при реальной нехватке диска. Firefox вытесняет наименее используемые источники, когда пул квот заполняется.
Два защитных паттерна:
- Записывайте любое состояние, которое нельзя воссоздать (ID сессий загрузки, смещения частичных чанков), в формат, дружественный к перезагрузке, чтобы свежая загрузка страницы могла получить данные с сервера и продолжить работу.
- Для долгоживущего состояния запрашивайте
navigator.storage.persist()и показывайте UI для подтверждения пользователем при запросе браузера.
Держите сервер источником истины для всего, что нельзя потерять. Браузерное хранилище — это быстрый кэш, который может исчезнуть в одночасье.
Подводные камни совместимости браузеров
Три ловушки встречаются снова и снова:
- Перечисление
indexedDB.databases()не поддерживается в Firefox (пользователи с «удалять куки при закрытии» теряют весь контент IndexedDB без событий). FileSystemFileHandle.queryPermission()ведёт себя по-разному после перезагрузок — иногда возвращает'prompt'даже при предоставленном разрешении. Всегда вызывайтеrequestPermission()защитно.- Приватный/инкогнито режим даёт всем трём API отдельную, меньшую, только для сессии квоту. Код, работающий в обычном режиме, может немедленно получить
QuotaExceededErrorв приватных окнах.
HexaTransfer использует IndexedDB для состояния сессий, OPFS для буферизации шифротекста при потоковом шифровании и File System Access API для расшифрованных загрузок в 10 ГБ на поддерживаемых браузерах. Попробуйте на hexatransfer.com — бесплатно, без аккаунта, до 10 ГБ.
Выбор стека для вашего приложения
Для большинства приложений передачи файлов правильная комбинация: обёртка idb над IndexedDB для метаданных, синхронные хэндлы OPFS для пространства шифрования/дешифровки, Cache API для app shell внутри Service Worker, File System Access API для финальных скачиваний со StreamSaver как запасным вариантом и вызов navigator.storage.persist() при онбординге. Это покрывает все актуальные браузеры, остаётся в рамках квот на мобильных устройствах и корректно восстанавливается при вытеснении. Создайте небольшие адаптеры вокруг каждого API — и когда OPFS получит новый метод или Safari поднимет квоту, вы измените один файл и выпустите обновление.
Безопасная отправка больших файлов со сквозным шифрованием
Передавайте файлы до 10 ГБ бесплатно со сквозным шифрованием. Регистрация не требуется. Ваши файлы шифруются в браузере перед загрузкой — никто другой не может их прочитать.
Отправить файл