Как сохранить файл на локальные устройства пользователя с помощью нового API доступа к файловой системе и резервного варианта для несовместимых браузеров.
Почти в каждом веб-приложении я повторно использую один и тот же шаблон для экспорта данных в файловую систему в JavaScript — то есть решение, которое использует API доступа к файловой системе и старую добрую функцию загрузки в качестве запасного варианта. Я подумал, что стоит написать об этом пост в качестве документации 😉.
Введение
API доступа к файловой системе позволяет читать, записывать и управлять файлами в вашем браузере. Это позволяет разработчикам создавать мощные веб-приложения, взаимодействующие с файлами на локальном устройстве пользователя.
У команды web.dev есть руководство, в котором представлены и выделены все функции.
Это относительно новый API, поэтому он еще не принят всеми поставщиками браузеров.
Например, одна из ключевых функций, которую мы собираемся использовать — showSaveFilePicker
, которая открывает диалоговое окно для выбора места назначения файла, который будет записан на локальный диск пользователя, — поддерживается только Edge, Chrome и Opera (февраль 2018 г.). 2022- источник Caniuse).
Начиная
Вообще говоря, я использую TypeScript. Это решение также обеспечивается безопасностью типов. Вот почему сначала необходимо установить определения типов для API доступа к файловой системе.
npm i -D @types/wicg-file-system-access
Руки вверх
Для экспорта файла я использую Blob
— то есть содержимое файла, который я хочу экспортировать — и filename
. Я создаю одну функцию, которая сохраняется на локальном устройстве пользователя и которую можно использовать в моем приложении.
export const save = (data: {blob: Blob, filename: string}) => { if ('showSaveFilePicker' in window) { return exportNativeFileSystem(data); } return download(data); };
API доступа к файловой системе — Сохранить как
Вышеуказанная функция проверяет, доступен ли showSaveFilePicker
в объекте window
, т. е. проверяет, поддерживает ли браузер API доступа к файловой системе или нет.
Чтобы сохранить файл с новым API, мы сначала показываем пользователю диалог в режиме «сохранения». Используя его, пользователь может выбрать место, где будет сохранен файл. После установки пути файл можно эффективно записать на локальный диск.
const exportNativeFileSystem = async ({blob, filename}: {blob: Blob, filename: string}) => { const fileHandle: FileSystemFileHandle = await getNewFileHandle({filename}); if (!fileHandle) { throw new Error('Cannot access filesystem'); } await writeFile({fileHandle, blob}); };
Во многих случаях я хочу, чтобы мое приложение предлагало имя файла по умолчанию. Этого можно добиться, установив suggestedName
. Кроме того, я также ограничиваю типы файлов, которые можно выбрать, предоставляя MIME-типы и соответствующие расширения файлов.
const getNewFileHandle = ({filename}: {filename: string}): Promise<FileSystemFileHandle> => { const opts: SaveFilePickerOptions = { suggestedName: filename, types: [ { description: 'Markdown file', accept: { 'text/plain': ['.md'] } } ] }; return showSaveFilePicker(opts); };
Наконец, файл можно эффективно записать с помощью writeFile
— еще одной функции API. Он использует дескриптор файла, который я ранее запросил, чтобы узнать, куда экспортировать данные в файловой системе.
const writeFile = async ({fileHandle, blob}: {fileHandle: FileSystemFileHandle, blob: Blob}) => { const writer = await fileHandle.createWritable(); await writer.write(blob); await writer.close(); };
Резервный вариант — Скачать
В качестве запасного варианта я добавляю в DOM временный элемент привязки, который автоматически щелкается. Чтобы экспортировать файл в папку загрузки пользователя по умолчанию, я предоставляю объект в качестве URL-адреса для файла blob
.
const download = async ({filename, blob}: {filename: string; blob: Blob}) => { const a: HTMLAnchorElement = document.createElement('a'); a.style.display = 'none'; document.body.appendChild(a); const url: string = window.URL.createObjectURL(blob); a.href = url; a.download = `${filename}.md`; a.click(); window.URL.revokeObjectURL(url); a.parentElement?.removeChild(a); };
Получить код
Вы можете найти весь код, представленный в этой статье, в недавнем плагине Chrome, который я опубликовал на GitHub 👉 save-utils.ts
Краткое содержание
Это был довольно короткий пост, который, я надеюсь, был хоть немного интересным 🤪. Если вы хотите глубже изучить API доступа к файловой системе, я еще раз советую вам взглянуть на хороший пост команды web.dev.
В бесконечность и дальше
Дэвид