在早期我們經常聽到這樣的說法:浏覽器是一個沙盒,它不允許我們操作本地檔案,但是現在這個說法已經不再适用了,因為我們可以使用 File System Access API 來實作這個功能。
什麼是 File System Access API
File System Access API 是一項 Web API,允許 Web 應用程式從使用者裝置的本地檔案系統中讀取和寫入檔案。
它提供了一種簡單且安全的方法,讓使用者在不離開 Web 應用的情況下,從本地檔案系統中操作檔案。
這項 API 為 Web 應用程式提供了更多的靈活性和功能,使其更接近于本地應用程式的體驗。
File System Access API 遵循同源政策,隻允許 Web 應用程式在具有相同源的檔案系統上進行操作。
當使用者使用該 API 時,會提示使用者授權應用程式通路他們的檔案系統。
如果使用者授權,則應用程式可以使用該 API 通路使用者選擇的檔案和目錄。
使用 File System Access API 可以通路本地檔案系統,進而實作一些有用的功能,例如:
- 将檔案從本地檔案系統上傳到 Web 應用程式;
- 将 Web 應用程式中的資料寫入到本地檔案系統中;
- 在使用者的本地檔案系統上建立、重命名和删除檔案;
- 讀取本地檔案系統上的檔案内容。
如何使用 File System Access API
我不是很喜歡概念性的東西,上面的内容是網上借鑒的(文化人),我更喜歡直接上代碼,是以我們直接上代碼。
選擇檔案
首先我們來看看如何選擇檔案,這個功能是 File System Access API 中最基礎的功能,我們可以通過 showOpenFilePicker 方法來實作。
const fileHandle = await window.showOpenFilePicker();
console.log(fileHandle);
複制代碼
可以看到我們這裡使用了async/await文法,這是因為showOpenFilePicker異步方法,它會傳回一個Promise對象,我們可以通過await來等待它的結果。
showOpenFilePicker方法會傳回一個FileHandle對象,我們可以通過它來擷取檔案的資訊。
我們來看看它最後傳回的結果:
image.png
可以看到的是最後的結果是一個數組,這是因為我們可以選擇多個檔案;
而這個數組的每一項都是一個FileSystemFileHandle對象,我們可以通過它來擷取和操作檔案。
FileSystemFileHandle
FileSystemFileHandle對象是一個代表檔案的對象,它提供了一些方法來擷取和操作檔案。
FileSystemFileHandle提供了一些方法來擷取和操作檔案,例如:
- getFile:傳回一個Promise對象,用于擷取檔案;
- createSyncAccessHandle:傳回一個FileSystemSyncAccessHandle對象,用于同步通路檔案;
- createWritable:傳回一個Promise對象,用于建立一個可寫流,用于寫入檔案;
我們來看看如何使用getFile方法來擷取檔案。
const fileHandle = await window.showOpenFilePicker();
const file = await fileHandle[0].getFile();
console.log(file);
複制代碼
image.png
可以看到,我們通過getFile方法擷取到了檔案,它傳回的是一個File對象,我們可以通過它來擷取檔案的資訊。
都拿到File對象了,後面怎麼操作就很熟悉了吧,直接使用FileReader對象來擷取檔案内容,後面你愛怎麼操作就怎麼操作。
FileSystemHandle
通過截圖我們還看到了有kind屬性和name屬性,這兩個屬性是繼承自FileSystemHandle對象的。
FileSystemFileHandle繼承自FileSystemHandle,它是一個代表檔案系統中的檔案或目錄的對象。
FileSystemHandle提供了一些方法來擷取和操作檔案系統中的檔案或目錄,例如:
- kind:傳回一個字元串,用于表示檔案或目錄;
- name:傳回一個字元串,用于表示檔案或目錄的名稱;
- isSameEntry:傳回一個boolean值,用于表示兩個檔案或目錄是否相同;
- queryPermission:傳回一個Promise對象,用于查詢檔案或目錄的權限;
- requestPermission:傳回一個Promise對象,用于請求檔案或目錄的權限;
- remove:傳回一個Promise對象,用于删除檔案或目錄;
我們可以通過kind屬性來判斷目前的FileSystemHandle對象是檔案還是目錄。
const fileHandle = await window.showOpenFilePicker();
const file = await fileHandle[0].getFile();
console.log(fileHandle[0].kind);
複制代碼
當然我們使用的是showOpenFilePicker方法,是以它傳回的肯定是檔案,是以還有一個showDirectoryPicker方法,它可以用來選擇目錄。
選擇目錄
選擇目錄的方法和選擇檔案的方法是一樣的,隻是我們需要使用showDirectoryPicker方法。
const directoryHandle = await window.showDirectoryPicker();
console.log(directoryHandle);
複制代碼
image.png
可以看到,我們通過showDirectoryPicker方法擷取到了目錄,它傳回的是一個FileSystemDirectoryHandle對象,我們可以通過它來擷取和操作目錄。
使用showDirectoryPicker方法時,浏覽器會提示使用者授權應用程式通路他們的檔案系統,請不要拒絕喲。
FileSystemDirectoryHandle
FileSystemDirectoryHandle對象是一個代表檔案系統中的目錄的對象,它提供了一些方法來擷取和操作目錄。
FileSystemDirectoryHandle提供的方法就比較多了,例如:
- entries:傳回一個AsyncIterable對象,用于擷取目錄中的所有檔案和目錄;
- keys:傳回一個AsyncIterable對象,用于擷取目錄中的所有檔案和目錄的名稱;
- values:傳回一個AsyncIterable對象,用于擷取目錄中的所有檔案和目錄的FileSystemHandle對象;
- getFileHandle:傳回一個Promise對象,用于擷取目錄中的檔案;
- getDirectoryHandle:傳回一個Promise對象,用于擷取目錄中的目錄;
- removeEntry:傳回一個Promise對象,用于删除目錄中的檔案或目錄;
- resolve:傳回一個Promise對象,用于擷取目錄中的檔案或目錄;
entries、keys、values這三個方法都是用來擷取目錄中的所有檔案和目錄的,它們傳回的都是一個AsyncIterable對象,我們可以通過for await...of文法來周遊它。
const directoryHandle = await window.showDirectoryPicker();
for await (const [name, handle] of directoryHandle.entries()) {
if (handle.kind === 'file') {
console.log(name, 'file');
} else {
console.log(name, 'directory');
}
}
複制代碼
我們可以通過handle.kind來判斷目前的FileSystemHandle對象是檔案還是目錄。
而這裡的getFileHandle、getDirectoryHandle就是用來擷取目錄中的檔案和目錄的,它們都傳回一個Promise對象,我們可以通過await來擷取它們。
const directoryHandle = await window.showDirectoryPicker();
for await (const [name, handle] of directoryHandle.entries()) {
if (handle.kind === 'file') {
const fileHandle = await directoryHandle.getFileHandle(name);
console.log(fileHandle);
} else {
const directoryHandle = await directoryHandle.getDirectoryHandle(name);
console.log(directoryHandle);
}
}
複制代碼
這裡大家可以自己嘗試一下,我就不截圖了。
操作檔案
上面我們了解到了如何擷取檔案和目錄,那麼我們接下來就來看看如何操作檔案和目錄。
讀取檔案
讀取檔案做過檔案上傳的同學應該都很熟悉了,我們可以使用FileReader對象來讀取檔案。
const fileHandle = await window.showOpenFilePicker({
excludeAcceptAllOption: false,
types: [
{
description: 'Text files',
accept: {
'text/plain': ['.txt'],
},
},
],
});
const file = await fileHandle[0].getFile();
const reader = new FileReader();
reader.onload = () => {
console.log(reader.result);
};
reader.readAsText(file);
複制代碼
這裡我們在使用showOpenFilePicker方法時,我們通過types屬性來限制檔案的類型,這樣使用者就隻能選擇文本檔案了。
showOpenFilePicker還有其他的屬性,例如:
- multiple:一個boolean值,預設為false,是否允許使用者選擇多個檔案;
- excludeAcceptAllOption:一個boolean值,預設為false,是否允許使用者選擇所有類型的檔案(就是選擇檔案下拉的所有檔案選項);
- types:一個數組,用于限制使用者選擇的檔案類型(就是選擇檔案的下拉選項);
- description:一個字元串,用于描述檔案類型(就是下拉選項的文字);
- accept:一個對象,用于描述檔案類型(就是控制選擇檔案的類型,例如image/*表示圖檔類型),具體可以參考:MIME types[1];
寫入檔案
寫入檔案可以使用上面提到的FileSystemFileHandle對象的createWritable方法來建立一個FileSystemWritableFileStream對象,然後通過它來寫入檔案。
const fileHandle = await window.showSaveFilePicker({
types: [
{
description: 'Text files',
accept: {
'text/plain': ['.txt'],
},
},
],
});
// 建立一個可寫流
const writable = await fileHandle.createWritable();
// 寫入資料
await writable.write('Hello World!');
// 關閉流
await writable.close();
複制代碼
這裡我們使用showSaveFilePicker方法來建立一個檔案,然後通過createWritable方法來建立一個可寫流,然後通過write方法來寫入資料,最後通過close方法來關閉流。
showSaveFilePicker也是檔案選擇器的一種,它和showOpenFilePicker的差別在于,showSaveFilePicker是用來建立檔案的,而showOpenFilePicker是用來選擇檔案的。
showSaveFilePicker傳回的是新建立的檔案的FileSystemFileHandle對象,而showOpenFilePicker傳回的是選擇的檔案的FileSystemFileHandle對象。
注意:操作檔案流時,一定要記得關閉流喲,否則會導緻檔案鎖定,無法進行其他操作,做前端的同學可能對這一塊并不熟悉,是以特此提醒一下。
操作目錄
上面我們已經知道了如何操作檔案了,那麼接下來我們就來看看如何操作目錄。
建立目錄
建立目錄可以使用FileSystemDirectoryHandle對象的getDirectoryHandle方法來建立一個目錄。
const directoryHandle = await window.showDirectoryPicker();
const newDirectoryHandle = await directoryHandle.getDirectoryHandle('new-directory', {
create: true,
});
複制代碼
getDirectoryHandle方法接收兩個參數:
- name:一個字元串,用于指定目錄的名稱;
- options:一個對象,用于指定目錄的選項(可選);
- create:一個boolean值,預設為false,是否建立目錄;
目前隻有create一個選項,如果設定為true,則會建立一個目錄,如果設定為false,則會擷取一個目錄。
如果目錄不存在,且create為false,則會報錯。
删除目錄
删除目錄可以使用FileSystemDirectoryHandle對象的removeEntry方法來删除一個目錄。
const directoryHandle = await window.showDirectoryPicker();
await directoryHandle.removeEntry('new-directory');
複制代碼
removeEntry方法接收一個參數,一個字元串,用于指定要删除的目錄的名稱。
相容性
截止到現在,showDirectoryPicker和showOpenFilePicker這兩個方法在Chrome 86版本中已經可以正常使用了,但是在Firefox中還不支援。
下面是來自caniuse[2]的相容性資料:
image.png
雖然Firefox還不支援,但是在一些實驗性的項目上我們可以使用這些API,指定使用者使用Chrome浏覽器來通路。
總結
本文主要介紹了File System Access API的基本使用,包括如何擷取檔案和目錄,以及如何操作檔案和目錄。