ブラウザは長い間、ファイルやディレクトリを扱うことができました。 File API ウェブ アプリケーションでファイル オブジェクトを表す機能を提供します。 プログラムで選択してデータにアクセスできます でも、近くで見ると、キラキラしているものは金だけじゃない。
従来のファイル処理方法では
ファイルを開く
デベロッパーは、
<input type="file">
要素です。
最もシンプルな形式では、ファイルを開くコードは次のコードサンプルのようになります。
input
オブジェクトは FileList
を提供します。
以下のケースでは、1 つだけ
File
。
File
は、Blob
の特定の種類です。
blob で可能なあらゆるコンテキストで使用できます。
const openFile = async () => {
return new Promise((resolve) => {
const input = document.createElement('input');
input.type = 'file';
input.addEventListener('change', () => {
resolve(input.files[0]);
});
input.click();
});
};
ディレクトリを開く
フォルダ(またはディレクトリ)を開く場合、
<input webkitdirectory>
属性です。
それ以外はすべて上記と同じように機能します。
ベンダー プレフィックスが付いていても、
webkitdirectory
は、Chromium と WebKit のブラウザで使用できるだけでなく、従来の EdgeHTML ベースの Edge や Firefox でも使用できます。
ファイルをダウンロードする代わりに保存する
従来、ファイルを保存できるのはファイルのダウンロードに限られていました。
これは
<a download>
属性です。
blob の場合、アンカーの href
属性を blob:
URL に設定できます。URL は、
URL.createObjectURL()
メソッドを呼び出します。
const saveFile = async (blob) => {
const a = document.createElement('a');
a.download = 'my-file.txt';
a.href = URL.createObjectURL(blob);
a.addEventListener('click', (e) => {
setTimeout(() => URL.revokeObjectURL(a.href), 30 * 1000);
});
a.click();
};
問題
ダウンロード アプローチの大きな欠点は、従来のバージョンを つまり、元のファイルを上書きする方法はありません。 代わりに、元のファイルの新しいコピーが作成されます。 自動的にダウンロードされます。
File System Access API
File System Access API を使用すると、ファイルを開いて保存する際の操作が大幅に簡素化されます。 また、真の保存も可能になります。つまり、ファイルの保存場所を選択できるだけでなく、 既存のファイルを上書きすることもできます
ファイルを開く
File System Access API を使用すると、
ファイルを開くには、window.showOpenFilePicker()
メソッドを 1 回呼び出すだけです。
この呼び出しはファイル ハンドルを返し、getFile()
メソッドを通じて実際の File
を取得できます。
const openFile = async () => {
try {
// Always returns an array.
const [handle] = await window.showOpenFilePicker();
return handle.getFile();
} catch (err) {
console.error(err.name, err.message);
}
};
ディレクトリを開く
呼び出してディレクトリを開きます
window.showDirectoryPicker()
: ファイル ダイアログ ボックスでディレクトリを選択可能にします。
ファイルを保存中
ファイルの保存も同じように簡単です。
ファイル ハンドルから、createWritable()
を介して書き込み可能なストリームを作成します。
次に、ストリームの write()
メソッドを呼び出して Blob データを書き込みます。
最後に、close()
メソッドを呼び出してストリームを閉じます。
const saveFile = async (blob) => {
try {
const handle = await window.showSaveFilePicker({
types: [{
accept: {
// Omitted
},
}],
});
const writable = await handle.createWritable();
await writable.write(blob);
await writable.close();
return handle;
} catch (err) {
console.error(err.name, err.message);
}
};
browser-fs-access の概要
File System Access API と同様に まだ広く普及していません。
<ph type="x-smartling-placeholder">をご覧ください。File System Access API は漸進的な機能強化として表示されているのはそのためです。 ブラウザが対応していれば そうでない場合は従来のアプローチを使用します サポートされていない JavaScript コードを不必要にダウンロードすると、ユーザーに罰則が科されません。 browser-fs-access ライブラリが私の答えです。
デザイン理念
File System Access API は今後も変更される可能性があるため、
browser-fs-access API はそれをモデル化していません。
つまり、ライブラリはポリフィルではありません。
ポニーフィルです。
アプリを可能な限り小さくするために必要な機能はすべて、(静的または動的に)排他的にインポートできます。
利用可能なメソッドは、
fileOpen()
directoryOpen()
、
fileSave()
。
内部的には、このライブラリ機能は File System Access API がサポートされているかどうか、
対応するコードパスをインポートします
browser-fs-access ライブラリを使用する
この 3 つの方法は直感的に使用できます。
アプリが受け入れ可能な mimeTypes
またはファイル extensions
を指定し、multiple
フラグを設定できます。
複数のファイルやディレクトリの選択を許可または禁止できます。
詳しくは、
browser-fs-access API のドキュメント。
以下のコードサンプルは、画像ファイルを開いて保存する方法を示しています。
// The imported methods will use the File
// System Access API or a fallback implementation.
import {
fileOpen,
directoryOpen,
fileSave,
} from 'https://unpkg.com/browser-fs-access';
(async () => {
// Open an image file.
const blob = await fileOpen({
mimeTypes: ['image/*'],
});
// Open multiple image files.
const blobs = await fileOpen({
mimeTypes: ['image/*'],
multiple: true,
});
// Open all files in a directory,
// recursively including subdirectories.
const blobsInDirectory = await directoryOpen({
recursive: true
});
// Save a file.
await fileSave(blob, {
fileName: 'Untitled.png',
});
})();
デモ
上記のコードの実際の動作は、Glitch のデモでご覧いただけます。 そこで同様に、ソースコードを入手できます。 セキュリティ上の理由から、クロスオリジンのサブフレームではファイル選択ツールを表示できません。 この記事にはデモを埋め込むことはできません。
browser-fs-access ライブラリの実用化
自由時間には、仕事に少し インストール可能な PWA Excalidraw という 手描きのような図を簡単にスケッチできるホワイトボード ツールです。 応答性に優れ、小型のスマートフォンから大画面のパソコンにまで幅広く対応しています。 つまり、さまざまなプラットフォームでファイルを処理する必要がある File System Access API をサポートしているかどうかを確認できます。 そのため、browser-fs-access ライブラリには適しています。
たとえば、iPhone で描画を開始したり、 保存する(技術的にはダウンロードします。Safari は File System Access API をサポートしていないため) iPhone のダウンロード フォルダに移動して、デスクトップでファイルを開く(スマートフォンから転送した後に) 変更して上書きすることも、新しいファイルとして保存することもできます。
<ph type="x-smartling-placeholder">で確認できます。 <ph type="x-smartling-placeholder">で確認できます。 <ph type="x-smartling-placeholder">で確認できます。 <ph type="x-smartling-placeholder">実際のコードサンプル
以下は、Excalidraw で browser-fs-access を使用する実際の例です。
この抜粋は
/src/data/json.ts
。
特に注目すべき点は、saveAsJSON()
メソッドがファイル ハンドルまたは null
を browser-fs-access に渡す方法です。
fileSave()
メソッドを使用すると、ハンドルが渡されると上書きされますが、
保存されていない場合は新しいファイルに保存します。
export const saveAsJSON = async (
elements: readonly ExcalidrawElement[],
appState: AppState,
fileHandle: any,
) => {
const serialized = serializeAsJSON(elements, appState);
const blob = new Blob([serialized], {
type: "application/json",
});
const name = `${appState.name}.excalidraw`;
(window as any).handle = await fileSave(
blob,
{
fileName: name,
description: "Excalidraw file",
extensions: ["excalidraw"],
},
fileHandle || null,
);
};
export const loadFromJSON = async () => {
const blob = await fileOpen({
description: "Excalidraw files",
extensions: ["json", "excalidraw"],
mimeTypes: ["application/json"],
});
return loadFromBlob(blob);
};
UI に関する考慮事項
Excalidraw のアプリでも、
UI をブラウザのサポート状況に適応させる必要があります。
File System Access API がサポートされている場合(if ('showOpenFilePicker' in window) {}
)
[Save] ボタンに加えて [Save As] ボタンを表示できます。
以下のスクリーンショットは、iPhone と Chrome デスクトップの Excalidraw のレスポンシブ メインアプリのツールバーの違いを示しています。
iPhone では [名前を付けて保存] ボタンがありません。
まとめ
システム ファイルの操作は、技術的にはすべての最新のブラウザで機能します。 File System Access API をサポートするブラウザでは、 (ダウンロードだけでなく)真の意味での保存と上書きが可能で、 ユーザーはどこでも新しいファイルを作成できます File System Access API をサポートしていないブラウザでも機能します。 browser-fs-access で簡単に プログレッシブ エンハンスメントの細かな部分に対応し、コードをできる限りシンプルにします。
謝辞
この記事は Joe Medley によってレビューされ、 Kayce Basques。 Excalidraw への貢献に感謝します pull リクエストを確認してもらう必要があります。 ヒーロー画像: Ilya Pavlov、Unsplash より