JavaScript. Работа с большими файлами в браузере. Часть 2/2: Создание 5Gb файлов в браузере

от автора

Часть 1/2: Чтение файлов | Часть 2/2: Создание файлов

Онлайн доска DGRM.net кеширует файлы в постоянном кеше. Постоянный кеш не удаляется при закрытии вкладки. Рассказываю как создавать и хранить большие файлы в браузере.

Рис. 1. Расход памяти при полной загрузке файла в память

Рис. 1. Расход памяти при полной загрузке файла в память

Кеш файлов на 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. FireFox (1) file.bytes(). Расход памяти при полной загрузке файла в память

Рис. 2. FireFox (1) file.bytes(). Расход памяти при полной загрузке файла в память

Вариант (2) writable.write(blob)

(2) Быстро работает в Chrome — 23 сек. В FireFox очень медленно — больше минуты. Файл не грузится целиком в память, копируется кусочками — рис 3 и 4. В Chrome и FireFox графики похожие, только в FireFox растянутые по времени.

Рис 3. Chrome (2) writable.write(file). Расход памяти

Рис 3. Chrome (2) writable.write(file). Расход памяти
Рис 4. Chrome (2) wr.writable(file). Обращения к диску

Рис 4. Chrome (2) wr.writable(file). Обращения к диску

Вариант(3) blob.stream().pipeTo(writable)

Хорошо работает в Chrome и FireFox, В Chrome медленней чем (2). Графики похожи на (2). Вариант (3) с pipeTo оказался лучше — таблица 1.

Метод загрузки
Файл 5 Gb

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 аудио и видео. Использую для уроков по гитаре — удобно. Попробуйте.

Аудио и видео на онлайн доске DGRM.net

Аудио и видео на онлайн доске DGRM.net

ссылка на оригинал статьи https://habr.com/ru/articles/1040752/