Часть 1/2: Чтение файлов | Часть 2/2: Создание файлов
Онлайн доска DGRM.net кеширует файлы в постоянном кеше. Постоянный кеш не удаляется при закрытии вкладки. Рассказываю как создавать и хранить большие файлы в браузере.
Кеш файлов на origin private file system (OPFS)
Постоянный кеш файлов можно сделать с помощью cache API, indexedDB или OPFS. OPFS считается самым быстрым. OPFS это виртуальная файловая система. Можно делать файлы и папки. Другие сайты не будут иметь доступ к вашим файлам.
Запись файлов в OPFS
Получить ссылку на файл на устройстве пользователя можно с помощью HTMLInputElement. При этом данные файла не будут загружены в память.
/** * @param {string} [accept] * @returns {Promise<File>} */const fileInputOpen = accept => new Promise((resolve, reject) => { const input = document.createElement('input'); input.type = 'file'; input.multiple = false; input.accept = accept; input.style.display = 'none'; document.body.appendChild(input); const dispose = () => input?.remove(); input.oncancel = () => { resolve(null); dispose(); }; input.onchange = () => { resolve((!input.files?.length) ? null : input.files[0]); dispose(); }; input.click();});
Листинг 1. Получение ссылки на файл
Запись файла в OPFS — листинг 2.
/** * @param {string} fileName * @param {Blob} blob */const fileAdd = async (fileName, blob) => { const dir = await navigator.storage.getDirectory(); const writable = await (await dir.getFileHandle(fileName, { create: true })) .createWritable(); // (1) FireFox will load all file into memory // Chrome drop exception // await writable.write(await blob.bytes()); // (2) very slow in FireFox // await writable.write(blob); // (3) works in Chrome and FireFox. // In Chrome slower then (2) await blob.stream().pipeTo(writable, { preventClose: true }); await writable.close();};
Листинг 2. Запись файла в OPFS
В листинге 2 показаны 3 варианта копирования файла в OPFS. Проверял на файле в 5 Gb на Windows 11 и Ubuntu.
Вариант (1) blob.bytes()
Вариант (1) демонстрирует загрузку всего файла в память. Расход оперативной памяти четко видно — рис 2.. FireFox на Windows загрузит весь файл в память, в Ubuntu загрузит 2 Gb в память и бросит исключение. Chrome выбросит исключение.
Вариант (2) writable.write(blob)
(2) Быстро работает в Chrome — 23 сек. В FireFox очень медленно — больше минуты. Файл не грузится целиком в память, копируется кусочками — рис 3 и 4. В Chrome и FireFox графики похожие, только в FireFox растянутые по времени.
Вариант(3) blob.stream().pipeTo(writable)
Хорошо работает в Chrome и FireFox, В Chrome медленней чем (2). Графики похожи на (2). Вариант (3) с pipeTo оказался лучше — таблица 1.
|
Метод загрузки |
Chrome |
FireFox |
|
(1) blob.bytes() |
Исключение |
|
|
(2) writable.write(file) |
23.484 sec |
114.430 sec |
|
(3) blob.stream().pipeTo(writable |
30.391 sec |
34.675 sec |
Таблица 1. Сравнение методов создания файлов в OPFS
Запись части файла в OPFS
Для записи части файла в OPFS используется blob.slice(). Листиг 3 — запись части файла в OPFS без загрузки всего файла в память.
const file = await fileInputOpen(); if (!file) { return; } await fileAdd( file.name, // upload second byte file.slice(1, 2));
Листинг 3. Запись части файла в OPFS без загрузки всего файла в память
Как можно хранить много разных данных в одном большом файле см в первой части статьи.
Чтение из OPFS
Файл из OPFS можно вывести в картинку, <audio>, <video> – листинг 4.
const img = /** @type {HTMLImageElement} */(document.getElementById('img'));const dir = await navigator.storage.getDirectory();img.src = URL.createObjectURL( await (await dir.getFileHandle('1.png')).getFile());// don't forget to revoke object url when delete img// URL.revokeObjectURL(img.src)
Листинг 4. Отображение картинки из OPFS
Скачивание файла из OPFS – листинг 5.
/** * @param {Blob} blob * @param {string} name */const fileDownload = (blob, name) => { const link = document.createElement('a'); link.download = name; link.href = URL.createObjectURL(blob); link.click(); URL.revokeObjectURL(link.href); link.remove();};//// usageconst dir = await navigator.storage.getDirectory();fileDownload( await (await dir.getFileHandle('big.zip')).getFile(), 'big.zip');
Листинг 5. Скачивание файла из OPFS
Google говорит что таким способом можно скачать файлы до 1 Gb. В действительности 5 Gb скачивается без проблем и в Chrome и в FireFox в Windows и Ubuntu. Потребление оперативной памяти не растет.
Создание файлов на устройстве пользователя
Chrome может писать файлы прямо в файловой системе пользователя. Писать в файл можно кусочками, API тотже самый что и в OPFS – листинг 6.
if (window['showSaveFilePicker']) { /** @type {FileSystemFileHandle} */ const file = // @ts-ignore await window.showSaveFilePicker({ suggestedName: 'my.png' }); const writable = await file.createWritable(); // write your data // await blob1.stream().pipeTo(writable, { preventClose: true }); // await blob2.stream().pipeTo(writable, { preventClose: true }); await writable.close();}
Листинг 6. Создание файла в на устройстве пользователя
В других браузерах придется делать временный файл в OPFS и скачивать его с помощью ссылки – см. листинг 5.
Самореклама
Недавно добавил в DGRM.net аудио и видео. Использую для уроков по гитаре — удобно. Попробуйте.
ссылка на оригинал статьи https://habr.com/ru/articles/1040752/