[WebAPIs] File System
參考資料
- File System API @ MDN
- The File System Access API: simplifying access to local files @ Google Developer
- The origin private file system @ web.dev
基本概念
要操作資料夾或檔案,最重要的是需要取得對應的 Handler:
- 針對資料夾是
FileSystemDirectoryHandle
,這裡面可以再取得FileSystemDirectoryHandle
或FileSystemFileHandle
- 針對檔案是
FileSystemFileHandle
,這裡面可以再取得File
- 取得 Handle 後就可以使用對應的 API 來新增、刪除檔案
// Instance of FileSystemFileHandle object
FileSystemFileHandle {
kind: 'file';
name: 'test.txt';
}
// Instance of File object
File {
lastModified: 1693637969824;
lastModifiedDate: 'Sat Sep 02 2023 14:59:29 GMT+0800 (Taipei Standard Time) {}';
name: 'test.txt';
size: 10;
type: 'text/plain';
webkitRelativePath: '';
}
// Instance of FileSystemDirectoryHandle object
// Record<FileName, FileSystemHandle | FileSystemHandle>
FileSystemDirectoryHandle {
'Untitled.txt': {kind: 'file', name: 'Untitled.txt'}
'image.jpg': {kind: 'file', name: 'image.jpg'}
}
File System API 基本使用
處理檔案(FileSystemFileHandle)
透過 window.showOpenFilePicker()
這個方法,可以取得該檔案的 FileSystemFileHandle
,接著就可以使用:
fileHandle.getFile()
:取得 File 物件,接著可以進一步取得檔案內容fileHandle.createWritable()
:產生FileSystemWritableFileStream
,接著可以進一步建立檔案fileHandle.remove()
:移除檔案fileHandle.move()
:移動或重新命名檔案
讀取檔案(Read File)
// 定義讀取檔案的類型和能不能多選
const pickerOpts = {
types: [
{
description: 'Text Files',
accept: {
'text/plain': ['.txt'],
},
},
],
excludeAcceptAllOption: true,
multiple: false,
};
async function handleOpenFile() {
// fileHandles 是一個 array of FileSystemFileHandle
const fileHandles = await window.showOpenFilePicker(pickerOptions);
// 取得第一個檔案,把這個 FileSystemFileHandle 的 reference 存下來,之後可以用來存檔或其他檔案操作
const fileHandle = fileHandles[0];
// file 會是 File 物件,可以使用 .text()、.arrayBuffer()、.stream()、.slice() 等方法
const file = await fileHandle.getFile();
// 取得檔案內容
const contents = await file.text();
}
建立檔案/另存新檔(Save As)
const onSaveAs = async (fileData: string) => {
// 選擇檔案要儲存的位置
const fileHandle = await window.showSaveFilePicker(saveOptions);
await writeFile(fileHandle, fileData);
});
async function writeFile(fileHandle: FileSystemFileHandle, fileData: string) {
const writable = await fileHandle.createWritable();
await writable.write(fileData);
await writable.close();
}
處理資料夾(FileSystemDirectoryHandle)
透過 window.showDirectoryPicker()
這個方法,可以取得該檔案的 FileSystemDirectoryHandle
,接著就可以使用:
FileSystemDirectoryHandle
會是一個類似Record<FileName, FileSystemFileHandle | FileSystemDirectoryHandle>
的結構directoryHandle.entries()
、directoryHandle.keys()
、directoryHandle.values()
:疊代出需要的資訊directoryHandle.getDirectoryHandle()
、directoryHandle.getFileHandle()
:進一步「讀取」或「建立」該資料夾內的資料夾(FileSystemDirectoryHandle)或檔案(FileSystemFileHandle)directoryHandle.resolve(subHandle)
:解析 subHandle 相對於 directorHandle 的路徑directoryHandle.removeEntry()
:移除該資料夾中的其他資料夾或檔案directoryHandle.remove()
:移除資料夾
讀取資料夾
const onOpenDirectory = async () => {
// FileSystemDirectoryHandle 會是 Record<FileName, FileSystemFileHandle|FileSystemDirectoryHandle>
const directoryHandle = await window.showDirectoryPicker();
// 取得這個資料夾中的檔案與資料夾資訊
// entry: FileSystemFileHandle | FileSystemDirectoryHandle
for await (const entry of directoryHandle.values()) {
console.log({ kind: entry.kind, name: entry.name });
}
for await (const [name, handle] of directoryHandle) {
}
for await (const [name, handle] of directoryHandle.entries()) {
}
for await (const handle of directoryHandle.values()) {
}
for await (const name of directoryHandle.keys()) {
}
};
建立資料夾或檔案
const onOpenDirectory = async () => {
// FileSystemDirectoryHandle 會是 Record<FileName, FileSystemFileHandle|FileSystemDirectoryHandle>
const directoryHandle = await window.showDirectoryPicker();
// 讀取這個資料夾中名為 "My Notes.txt" 的檔案,如果這個資料夾中沒有 My Notes.txt 的檔案,就會自動建立一個
// FileSystemFileHandle
const fileHandle = await directoryHandle.getFileHandle('My Notes.txt', {
create: true,
});
// 取得相對於 directoryHandle,這個 fileHandle 的路徑
const path = await directoryHandle.resolve(fileHandle);
console.log(path);
// 讀取這個資料夾中名為 `foobar` 的資料夾,如果這個資料夾中沒有 foobar 的資料夾,就會自動建立一個
// FileSystemDirectoryHandle
const newDirectoryHandle = await directoryHandle.getDirectoryHandle('foobar', { create: true });
};