跳至主要内容

[WebAPIs] File System

參考資料

基本概念

要操作資料夾或檔案,最重要的是需要取得對應的 Handler:

  • 針對資料夾是 FileSystemDirectoryHandle,這裡面可以再取得 FileSystemDirectoryHandleFileSystemFileHandle
  • 針對檔案是 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 });
};