File System Access API ช่วยให้เว็บแอปอ่านหรือบันทึกการเปลี่ยนแปลงลงในไฟล์และโฟลเดอร์ในอุปกรณ์ของผู้ใช้โดยตรงได้
File System Access API คืออะไร
File System Access API ช่วยให้นักพัฒนาแอปสามารถสร้างเว็บแอปที่มีประสิทธิภาพซึ่งโต้ตอบกับไฟล์ในอุปกรณ์เครื่องของผู้ใช้ เช่น IDE, โปรแกรมแก้ไขรูปภาพและวิดีโอ, เครื่องมือแก้ไขข้อความ และอื่นๆ หลังจากผู้ใช้ให้สิทธิ์เข้าถึงเว็บแอปแล้ว API นี้จะอนุญาตให้เว็บแอปอ่านหรือบันทึกการเปลี่ยนแปลงลงในไฟล์และโฟลเดอร์ในอุปกรณ์ของผู้ใช้โดยตรง นอกจากการอ่านและเขียนไฟล์แล้ว File System Access API ยังมีความสามารถในการเปิดไดเรกทอรีและแสดงรายการเนื้อหา
หากคุณเคยอ่านและเขียนไฟล์มาก่อน ข้อมูลส่วนใหญ่ที่เราจะแชร์จะดูคุ้นเคย เราขอแนะนำให้คุณอ่านบทความดังกล่าวอยู่ดี เนื่องจากระบบบางระบบอาจไม่เหมือนกัน
เบราว์เซอร์ Chromium ส่วนใหญ่ใน Windows, macOS, ChromeOS และ Linux รองรับ File System Access API ข้อยกเว้นที่น่าสังเกตคือ Brave ซึ่งขณะนี้ใช้ได้เฉพาะหลัง Flag เรากําลังดำเนินการรองรับ Android ในบริบทของ crbug.com/1011535
การใช้ File System Access API
เราได้เขียนไฟล์ text editor ไฟล์เดียวเพื่อแสดงประสิทธิภาพและประโยชน์ของ File System Access API ซึ่งช่วยให้คุณเปิดไฟล์ข้อความ แก้ไข บันทึกการเปลี่ยนแปลงกลับไปยังดิสก์ หรือเริ่มไฟล์ใหม่และบันทึกการเปลี่ยนแปลงลงในดิสก์ได้ เนื้อหาไม่ได้ซับซ้อน แต่ให้ข้อมูลเพียงพอที่จะช่วยให้คุณเข้าใจแนวคิด
การสนับสนุนเบราว์เซอร์
การตรวจหาองค์ประกอบ
หากต้องการทราบว่าระบบรองรับ File System Access API หรือไม่ ให้ตรวจสอบว่ามีเมธอดเครื่องมือเลือกที่คุณสนใจหรือไม่
if ('showOpenFilePicker' in self) {
// The `showOpenFilePicker()` method of the File System Access API is supported.
}
ลองใช้
ดู File System Access API ใช้งานได้จริงในตัวอย่างเครื่องมือแก้ไขข้อความ
อ่านไฟล์จากระบบไฟล์ในเครื่อง
กรณีการใช้งานแรกที่ฉันต้องการจัดการคือการขอให้ผู้ใช้เลือกไฟล์ จากนั้นเปิดและอ่านไฟล์นั้นจากดิสก์
ขอให้ผู้ใช้เลือกไฟล์ที่จะอ่าน
จุดแรกเข้าของ File System Access API คือ window.showOpenFilePicker()
เมื่อเรียกใช้ ฟีเจอร์นี้จะแสดงกล่องโต้ตอบเครื่องมือเลือกไฟล์ และแจ้งให้ผู้ใช้เลือกไฟล์ หลังจากผู้ใช้เลือกไฟล์แล้ว API จะแสดงผลอาร์เรย์ของตัวแฮนเดิลไฟล์ พารามิเตอร์ options
(ไม่บังคับ) ช่วยให้คุณควบคุมลักษณะการทํางานของเครื่องมือเลือกไฟล์ได้ เช่น อนุญาตให้ผู้ใช้เลือกไฟล์ ไดเรกทอรี หรือไฟล์ประเภทต่างๆ ได้หลายรายการ
หากไม่ได้ระบุตัวเลือกใดๆ เครื่องมือเลือกไฟล์จะอนุญาตให้ผู้ใช้เลือกไฟล์ได้เพียงไฟล์เดียว ซึ่งเหมาะที่จะใช้เครื่องมือแก้ไขข้อความ
เช่นเดียวกับ API ที่มีประสิทธิภาพอื่นๆ อีกมากมาย การเรียก showOpenFilePicker()
จะต้องดำเนินการในบริบทที่ปลอดภัย และต้องเรียกจากภายในท่าทางสัมผัสของผู้ใช้
let fileHandle;
butOpenFile.addEventListener('click', async () => {
// Destructure the one-element array.
[fileHandle] = await window.showOpenFilePicker();
// Do something with the file handle.
});
เมื่อผู้ใช้เลือกไฟล์แล้ว showOpenFilePicker()
จะแสดงผลอาร์เรย์ของแฮนเดิล ซึ่งในกรณีนี้คืออาร์เรย์องค์ประกอบเดียวที่มี FileSystemFileHandle
รายการเดียวที่มีพร็อพเพอร์ตี้และเมธอดที่จําเป็นสําหรับโต้ตอบกับไฟล์
คุณควรเก็บการอ้างอิงแฮนเดิลไฟล์ไว้เพื่อใช้ในภายหลัง คุณต้องใช้สิทธิ์นี้เพื่อบันทึกการเปลี่ยนแปลงในไฟล์หรือดำเนินการอื่นๆ กับไฟล์
อ่านไฟล์จากระบบไฟล์
เมื่อคุณมีแฮนเดิลของไฟล์แล้ว คุณจะได้รับพร็อพเพอร์ตี้ของไฟล์หรือเข้าถึงไฟล์นั้นได้
ในระหว่างนี้ เราจะอ่านเนื้อหาของคำขอ การเรียก handle.getFile()
จะแสดงผลออบเจ็กต์ File
ซึ่งมี Blob หากต้องการรับข้อมูลจาก Blob ให้เรียกเมธอดใดเมธอดหนึ่ง (slice()
,
stream()
,
text()
หรือ
arrayBuffer()
)
const file = await fileHandle.getFile();
const contents = await file.text();
ออบเจ็กต์ File
ที่ FileSystemFileHandle.getFile()
แสดงผลจะอ่านได้ก็ต่อเมื่อไฟล์พื้นฐานบนดิสก์ไม่มีการเปลี่ยนแปลง หากไฟล์ในดิสก์ได้รับการแก้ไข ออบเจ็กต์ File
จะอ่านไม่ได้ และคุณจะต้องเรียกใช้ getFile()
อีกครั้งเพื่อรับออบเจ็กต์ File
ใหม่เพื่ออ่านข้อมูลที่เปลี่ยนแปลง
สรุปข้อมูลทั้งหมด
เมื่อผู้ใช้คลิกปุ่มเปิดเบราว์เซอร์จะแสดงเครื่องมือเลือกไฟล์ เมื่อเลือกไฟล์แล้ว แอปจะอ่านเนื้อหาและใส่ลงใน <textarea>
let fileHandle;
butOpenFile.addEventListener('click', async () => {
[fileHandle] = await window.showOpenFilePicker();
const file = await fileHandle.getFile();
const contents = await file.text();
textArea.value = contents;
});
เขียนไฟล์ลงในระบบไฟล์ในเครื่อง
ในเครื่องมือแก้ไขข้อความ คุณจะบันทึกไฟล์ได้ 2 วิธี ได้แก่ บันทึกและบันทึกเป็น บันทึกจะเขียนการเปลี่ยนแปลงกลับไปยังไฟล์ต้นฉบับโดยใช้แฮนเดิลไฟล์ที่ดึงข้อมูลไว้ก่อนหน้านี้ แต่บันทึก เป็นจะสร้างไฟล์ใหม่ จึงต้องใช้ตัวแฮนเดิลไฟล์ใหม่
สร้างไฟล์ใหม่
หากต้องการบันทึกไฟล์ ให้เรียกใช้ showSaveFilePicker()
ซึ่งจะแสดงเครื่องมือเลือกไฟล์ในโหมด "บันทึก" ซึ่งช่วยให้ผู้ใช้เลือกไฟล์ใหม่ที่ต้องการใช้บันทึกได้ สำหรับเครื่องมือแก้ไขข้อความ ฉันต้องการให้เพิ่มส่วนขยาย .txt
โดยอัตโนมัติด้วย จึงระบุพารามิเตอร์เพิ่มเติม
async function getNewFileHandle() {
const options = {
types: [
{
description: 'Text Files',
accept: {
'text/plain': ['.txt'],
},
},
],
};
const handle = await window.showSaveFilePicker(options);
return handle;
}
บันทึกการเปลี่ยนแปลงลงในดิสก์
คุณดูโค้ดทั้งหมดสําหรับการบันทึกการเปลี่ยนแปลงในไฟล์ได้ในตัวอย่างเครื่องมือแก้ไขข้อความของฉันใน GitHub การโต้ตอบหลักของระบบไฟล์อยู่ใน fs-helpers.js
กระบวนการที่ง่ายที่สุดจะมีลักษณะเป็นโค้ดต่อไปนี้
เราจะอธิบายแต่ละขั้นตอนให้ฟัง
// fileHandle is an instance of FileSystemFileHandle..
async function writeFile(fileHandle, contents) {
// Create a FileSystemWritableFileStream to write to.
const writable = await fileHandle.createWritable();
// Write the contents of the file to the stream.
await writable.write(contents);
// Close the file and write the contents to disk.
await writable.close();
}
การเขียนข้อมูลลงในดิสก์ใช้ออบเจ็กต์ FileSystemWritableFileStream
ซึ่งเป็นคลาสย่อยของ WritableStream
สร้างสตรีมโดยเรียกใช้ createWritable()
ในออบเจ็กต์ตัวแฮนเดิลไฟล์ เมื่อเรียก createWritable()
เบราว์เซอร์จะตรวจสอบก่อนว่าผู้ใช้ได้ให้สิทธิ์เขียนไฟล์หรือไม่ หากยังไม่ได้ให้สิทธิ์การเขียน เบราว์เซอร์จะแจ้งให้ผู้ใช้ขอสิทธิ์ หากไม่ได้รับสิทธิ์ createWritable()
จะแสดงข้อผิดพลาด DOMException
และแอปจะเขียนลงในไฟล์ไม่ได้ ในเครื่องมือแก้ไขข้อความ ระบบจะจัดการออบเจ็กต์ DOMException
ในเมธอด saveFile()
เมธอด write()
ใช้สตริง ซึ่งเป็นสิ่งที่จำเป็นสำหรับเครื่องมือแก้ไขข้อความ แต่ก็สามารถรับ BufferSource หรือ Blob ได้ด้วย เช่น คุณสามารถส่งผ่านสตรีมไปยัง stdin ได้โดยตรงโดยทำดังนี้
async function writeURLToFile(fileHandle, url) {
// Create a FileSystemWritableFileStream to write to.
const writable = await fileHandle.createWritable();
// Make an HTTP request for the contents.
const response = await fetch(url);
// Stream the response into the file.
await response.body.pipeTo(writable);
// pipeTo() closes the destination pipe by default, no need to close it.
}
นอกจากนี้ คุณยังกด seek()
หรือ truncate()
ภายในสตรีมเพื่ออัปเดตไฟล์ที่ตำแหน่งที่ต้องการ หรือปรับขนาดไฟล์ได้ด้วย
การระบุชื่อไฟล์และไดเรกทอรีเริ่มต้นที่แนะนำ
ในหลายกรณี คุณอาจต้องการให้แอปแนะนำชื่อไฟล์หรือตำแหน่งเริ่มต้น เช่น เครื่องมือแก้ไขข้อความอาจต้องการแนะนำชื่อไฟล์เริ่มต้นเป็น Untitled Text.txt
แทน Untitled
ซึ่งทำได้โดยการส่งพร็อพเพอร์ตี้ suggestedName
เป็นส่วนหนึ่งของตัวเลือก showSaveFilePicker
const fileHandle = await self.showSaveFilePicker({
suggestedName: 'Untitled Text.txt',
types: [{
description: 'Text documents',
accept: {
'text/plain': ['.txt'],
},
}],
});
เช่นเดียวกับไดเรกทอรีเริ่มต้น หากกำลังสร้างเครื่องมือแก้ไขข้อความ คุณอาจต้องเริ่มกล่องโต้ตอบบันทึกไฟล์หรือเปิดไฟล์ในโฟลเดอร์ documents
เริ่มต้น ส่วนเครื่องมือแก้ไขรูปภาพอาจต้องเริ่มในโฟลเดอร์ pictures
เริ่มต้น คุณสามารถแนะนําไดเรกทอรีเริ่มต้นได้โดยส่งพร็อพเพอร์ตี้ startIn
ไปยังเมธอด showSaveFilePicker
, showDirectoryPicker()
หรือ showOpenFilePicker
ดังนี้
const fileHandle = await self.showOpenFilePicker({
startIn: 'pictures'
});
รายการไดเรกทอรีระบบที่รู้จักมีดังนี้
desktop
: ไดเรกทอรีเดสก์ท็อปของผู้ใช้ หากมีdocuments
: ไดเรกทอรีที่ระบบมักจะจัดเก็บเอกสารที่ผู้ใช้สร้างขึ้นdownloads
: ไดเรกทอรีที่มักใช้จัดเก็บไฟล์ที่ดาวน์โหลดmusic
: ไดเรกทอรีที่มักใช้จัดเก็บไฟล์เสียงpictures
: ไดเรกทอรีที่มักใช้เก็บรูปภาพและภาพนิ่งอื่นๆvideos
: ไดเรกทอรีที่มักใช้จัดเก็บวิดีโอหรือภาพยนตร์
นอกจากไดเรกทอรีของระบบที่รู้จักกันดีแล้ว คุณยังส่งตัวแฮนเดิลไฟล์หรือไดเรกทอรีที่มีอยู่เป็นค่าสำหรับ startIn
ได้ด้วย จากนั้นกล่องโต้ตอบจะเปิดขึ้นในไดเรกทอรีเดียวกัน
// Assume `directoryHandle` is a handle to a previously opened directory.
const fileHandle = await self.showOpenFilePicker({
startIn: directoryHandle
});
การระบุวัตถุประสงค์ของเครื่องมือเลือกไฟล์ต่างๆ
บางครั้งแอปพลิเคชันจะมีเครื่องมือเลือกที่แตกต่างกันสำหรับวัตถุประสงค์ที่แตกต่างกัน เช่น เครื่องมือแก้ไข Rich Text อาจอนุญาตให้ผู้ใช้เปิดไฟล์ข้อความและนำเข้ารูปภาพได้ด้วย โดยค่าเริ่มต้น เครื่องมือเลือกไฟล์แต่ละรายการจะเปิดขึ้นที่ตำแหน่งที่ระบบจดจำไว้ล่าสุด คุณหลีกเลี่ยงปัญหานี้ได้ด้วยการจัดเก็บค่า id
สำหรับเครื่องมือเลือกแต่ละประเภท หากระบุ id
ไว้ การติดตั้งใช้งานเครื่องมือเลือกไฟล์จะจดจำไดเรกทอรีที่ใช้ล่าสุดแยกต่างหากสำหรับ id
นั้น
const fileHandle1 = await self.showSaveFilePicker({
id: 'openText',
});
const fileHandle2 = await self.showSaveFilePicker({
id: 'importImage',
});
การจัดเก็บตัวแฮนเดิลไฟล์หรือตัวแฮนเดิลไดเรกทอรีใน IndexedDB
แฮนเดิลไฟล์และแฮนเดิลไดเรกทอรีเป็นข้อมูลที่จัดเรียงได้ ซึ่งหมายความว่าคุณสามารถบันทึกแฮนเดิลไฟล์หรือไดเรกทอรีลงใน IndexedDB หรือเรียกใช้ postMessage()
เพื่อส่งแฮนเดิลเหล่านั้นระหว่างต้นทางระดับบนสุดเดียวกัน
การจัดเก็บแฮนเดิลไฟล์หรือไดเรกทอรีไว้ใน IndexedDB หมายความว่าคุณสามารถจัดเก็บสถานะหรือจดจำไฟล์หรือไดเรกทอรีที่ผู้ใช้กำลังทำงานอยู่ ซึ่งช่วยให้คุณเก็บรายการไฟล์ที่เพิ่งเปิดหรือแก้ไขไว้ได้ เสนอให้เปิดไฟล์ล่าสุดอีกครั้งเมื่อเปิดแอป กู้คืนไดเรกทอรีที่ทำงานอยู่ก่อนหน้านี้ และอื่นๆ ในเครื่องมือแก้ไขข้อความ เราจะจัดเก็บรายการไฟล์ล่าสุด 5 รายการที่ผู้ใช้เปิดไว้ เพื่อให้เข้าถึงไฟล์เหล่านั้นได้อีกครั้ง
ตัวอย่างโค้ดต่อไปนี้แสดงการจัดเก็บและการเรียกข้อมูลแฮนเดิลไฟล์และแฮนเดิลไดเรกทอรี คุณสามารถดูการทํางานจริงได้ใน Glitch (เราใช้ไลบรารี idb-keyval เพื่อความกระชับ)
import { get, set } from 'https://unpkg.com/idb-keyval@5.0.2/dist/esm/index.js';
const pre1 = document.querySelector('pre.file');
const pre2 = document.querySelector('pre.directory');
const button1 = document.querySelector('button.file');
const button2 = document.querySelector('button.directory');
// File handle
button1.addEventListener('click', async () => {
try {
const fileHandleOrUndefined = await get('file');
if (fileHandleOrUndefined) {
pre1.textContent = `Retrieved file handle "${fileHandleOrUndefined.name}" from IndexedDB.`;
return;
}
const [fileHandle] = await window.showOpenFilePicker();
await set('file', fileHandle);
pre1.textContent = `Stored file handle for "${fileHandle.name}" in IndexedDB.`;
} catch (error) {
alert(error.name, error.message);
}
});
// Directory handle
button2.addEventListener('click', async () => {
try {
const directoryHandleOrUndefined = await get('directory');
if (directoryHandleOrUndefined) {
pre2.textContent = `Retrieved directroy handle "${directoryHandleOrUndefined.name}" from IndexedDB.`;
return;
}
const directoryHandle = await window.showDirectoryPicker();
await set('directory', directoryHandle);
pre2.textContent = `Stored directory handle for "${directoryHandle.name}" in IndexedDB.`;
} catch (error) {
alert(error.name, error.message);
}
});
แฮนเดิลและสิทธิ์ของไฟล์หรือไดเรกทอรีที่เก็บไว้
เนื่องจากสิทธิ์จะไม่คงอยู่ระหว่างเซสชันเสมอไป คุณจึงควรตรวจสอบว่าผู้ใช้ได้ให้สิทธิ์แก่ไฟล์หรือไดเรกทอรีโดยใช้ queryPermission()
หรือไม่ หากยังไม่ได้ดำเนินการ ให้โทรไปที่ requestPermission()
เพื่อขอ (อีกครั้ง) ซึ่งจะใช้ได้กับแฮนเดิลไฟล์และไดเรกทอรีด้วย คุณต้องเรียกใช้ fileOrDirectoryHandle.requestPermission(descriptor)
หรือ fileOrDirectoryHandle.queryPermission(descriptor)
ตามลำดับ
ในเครื่องมือแก้ไขข้อความ เราได้สร้างเมธอด verifyPermission()
ที่ตรวจสอบว่าผู้ใช้ได้ให้สิทธิ์แล้วหรือยัง และส่งคำขอหากจำเป็น
async function verifyPermission(fileHandle, readWrite) {
const options = {};
if (readWrite) {
options.mode = 'readwrite';
}
// Check if permission was already granted. If so, return true.
if ((await fileHandle.queryPermission(options)) === 'granted') {
return true;
}
// Request permission. If the user grants permission, return true.
if ((await fileHandle.requestPermission(options)) === 'granted') {
return true;
}
// The user didn't grant permission, so return false.
return false;
}
การขอสิทธิ์เขียนพร้อมกับคำขออ่านทำให้ฉันลดจำนวนข้อความแจ้งสิทธิ์ได้ ผู้ใช้จะเห็นข้อความแจ้งเพียงรายการเดียวเมื่อเปิดไฟล์ และจะให้สิทธิ์ทั้งอ่านและเขียนไฟล์
การเปิดไดเรกทอรีและการแจกแจงเนื้อหา
หากต้องการแจกแจงไฟล์ทั้งหมดในไดเรกทอรี ให้เรียกใช้ showDirectoryPicker()
ผู้ใช้เลือกไดเรกทอรีในเครื่องมือเลือก จากนั้นระบบจะแสดง FileSystemDirectoryHandle
ซึ่งช่วยให้คุณแจกแจงและเข้าถึงไฟล์ของไดเรกทอรีได้ โดยค่าเริ่มต้น คุณจะมีสิทธิ์อ่านไฟล์ในไดเรกทอรี แต่หากต้องการสิทธิ์เขียน ให้ส่ง { mode: 'readwrite' }
ไปยังเมธอด
butDir.addEventListener('click', async () => {
const dirHandle = await window.showDirectoryPicker();
for await (const entry of dirHandle.values()) {
console.log(entry.kind, entry.name);
}
});
หากต้องการเข้าถึงไฟล์แต่ละไฟล์โดยใช้ getFile()
เพิ่มเติม เช่น เพื่อดูขนาดไฟล์แต่ละไฟล์ อย่าใช้ await
กับผลลัพธ์แต่ละรายการตามลำดับ แต่ให้ประมวลผลไฟล์ทั้งหมดพร้อมกัน เช่น โดยใช้ Promise.all()
butDir.addEventListener('click', async () => {
const dirHandle = await window.showDirectoryPicker();
const promises = [];
for await (const entry of dirHandle.values()) {
if (entry.kind !== 'file') {
continue;
}
promises.push(entry.getFile().then((file) => `${file.name} (${file.size})`));
}
console.log(await Promise.all(promises));
});
การสร้างหรือเข้าถึงไฟล์และโฟลเดอร์ในไดเรกทอรี
จากไดเรกทอรี คุณสามารถสร้างหรือเข้าถึงไฟล์และโฟลเดอร์ได้โดยใช้เมธอด getFileHandle()
หรือ getDirectoryHandle()
ตามลำดับ การส่งออบเจ็กต์ options
(ไม่บังคับ) ที่มีคีย์เป็น create
และค่าบูลีนเป็น true
หรือ false
จะช่วยให้คุณระบุได้ว่าควรสร้างไฟล์หรือโฟลเดอร์ใหม่หรือไม่หากไม่มีอยู่
// In an existing directory, create a new directory named "My Documents".
const newDirectoryHandle = await existingDirectoryHandle.getDirectoryHandle('My Documents', {
create: true,
});
// In this new directory, create a file named "My Notes.txt".
const newFileHandle = await newDirectoryHandle.getFileHandle('My Notes.txt', { create: true });
การแก้ไขเส้นทางของรายการในไดเรกทอรี
เมื่อทำงานกับไฟล์หรือโฟลเดอร์ในไดเรกทอรี การแก้ไขเส้นทางของรายการที่เป็นปัญหาอาจมีประโยชน์ ซึ่งทำได้ด้วยเมธอด resolve()
ที่เหมาะเจาะ สำหรับการแก้ไข รายการอาจเป็นรายการย่อยโดยตรงหรือโดยอ้อมของไดเรกทอรี
// Resolve the path of the previously created file called "My Notes.txt".
const path = await newDirectoryHandle.resolve(newFileHandle);
// `path` is now ["My Documents", "My Notes.txt"]
การลบไฟล์และโฟลเดอร์ในไดเรกทอรี
หากได้รับสิทธิ์เข้าถึงไดเรกทอรี คุณจะลบไฟล์และโฟลเดอร์ที่อยู่ในไดเรกทอรีได้ด้วยวิธี removeEntry()
สําหรับโฟลเดอร์ คุณสามารถลบแบบทําซ้ำและรวมโฟลเดอร์ย่อยและไฟล์ทั้งหมดที่อยู่ในโฟลเดอร์นั้นๆ ได้ด้วย
// Delete a file.
await directoryHandle.removeEntry('Abandoned Projects.txt');
// Recursively delete a folder.
await directoryHandle.removeEntry('Old Stuff', { recursive: true });
การลบไฟล์หรือโฟลเดอร์โดยตรง
หากมีสิทธิ์เข้าถึงแฮนเดิลไฟล์หรือไดเรกทอรี ให้เรียกใช้ remove()
ใน FileSystemFileHandle
หรือ
FileSystemDirectoryHandle
เพื่อนำออก
// Delete a file.
await fileHandle.remove();
// Delete a directory.
await directoryHandle.remove();
การเปลี่ยนชื่อและย้ายไฟล์และโฟลเดอร์
คุณเปลี่ยนชื่อหรือย้ายไฟล์และโฟลเดอร์ไปยังตำแหน่งใหม่ได้โดยเรียกใช้ move()
ในอินเทอร์เฟซ FileSystemHandle
FileSystemHandle
มีอินเทอร์เฟซย่อย FileSystemFileHandle
และ
FileSystemDirectoryHandle
เมธอด move()
จะใช้พารามิเตอร์ 1 หรือ 2 รายการ รายการแรกอาจเป็นสตริงที่มีชื่อใหม่หรือ FileSystemDirectoryHandle
ไปยังโฟลเดอร์ปลายทางก็ได้ ในกรณีหลัง พารามิเตอร์ที่ 2 (ไม่บังคับ) คือสตริงที่มีชื่อใหม่เพื่อให้ย้ายและเปลี่ยนชื่อได้ในขั้นตอนเดียว
// Rename the file.
await file.move('new_name');
// Move the file to a new directory.
await file.move(directory);
// Move the file to a new directory and rename it.
await file.move(directory, 'newer_name');
การผสานรวมแบบลากและวาง
อินเทอร์เฟซการลากและวาง HTML ช่วยให้เว็บแอปพลิเคชันยอมรับไฟล์ที่ลากและวางในหน้าเว็บได้ ในระหว่างการลากและวาง ระบบจะเชื่อมโยงรายการไฟล์และไดเรกทอรีที่ลากมากับรายการไฟล์และรายการไดเรกทอรีตามลำดับ DataTransferItem.getAsFileSystemHandle()
เมธอดจะแสดงผลพรอมิสที่มีออบเจ็กต์ FileSystemFileHandle
หากรายการที่ลากเป็นไฟล์ และพรอมิสที่มีออบเจ็กต์ FileSystemDirectoryHandle
หากรายการที่ลากเป็นไดเรกทอรี ข้อมูลต่อไปนี้แสดงการใช้งาน โปรดทราบว่า DataTransferItem.kind
ของอินเทอร์เฟซการลากและวางคือ "file"
สำหรับทั้งไฟล์และไดเรกทอรี ส่วน FileSystemHandle.kind
ของ File System Access API คือ "file"
สำหรับไฟล์และ "directory"
สำหรับไดเรกทอรี
elem.addEventListener('dragover', (e) => {
// Prevent navigation.
e.preventDefault();
});
elem.addEventListener('drop', async (e) => {
e.preventDefault();
const fileHandlesPromises = [...e.dataTransfer.items]
.filter((item) => item.kind === 'file')
.map((item) => item.getAsFileSystemHandle());
for await (const handle of fileHandlesPromises) {
if (handle.kind === 'directory') {
console.log(`Directory: ${handle.name}`);
} else {
console.log(`File: ${handle.name}`);
}
}
});
การเข้าถึงระบบไฟล์ส่วนตัวของต้นทาง
ระบบไฟล์ส่วนตัวของต้นทางคือปลายทางพื้นที่เก็บข้อมูลที่เป็นส่วนตัวสำหรับต้นทางของหน้าเว็บ ดังที่ชื่อบอกไว้ แม้ว่าโดยทั่วไปเบราว์เซอร์จะใช้วิธีนี้โดยเก็บเนื้อหาของระบบไฟล์ส่วนตัวต้นทางนี้ไว้ในดิสก์ แต่ก็ไม่ได้ตั้งใจให้ผู้ใช้เข้าถึงเนื้อหาได้ ในทำนองเดียวกัน ไม่มีความคาดหวังว่าจะมีไฟล์หรือไดเรกทอรีที่มีชื่อตรงกับชื่อของไฟล์ย่อยในระบบไฟล์ส่วนตัวต้นทาง แม้ว่าเบราว์เซอร์อาจทำให้ดูเหมือนว่ามีไฟล์ แต่ภายในนั้นเบราว์เซอร์อาจจัดเก็บ "ไฟล์" เหล่านี้ไว้ในฐานข้อมูลหรือโครงสร้างข้อมูลอื่นๆ เนื่องจากเป็นระบบไฟล์ส่วนตัวต้นทาง สรุปคือ หากคุณใช้ API นี้ อย่าคาดหวังว่าจะพบไฟล์ที่สร้างขึ้นซึ่งจับคู่กันแบบ 1:1 บนฮาร์ดดิสก์ คุณจะดำเนินการได้ตามปกติในระบบไฟล์ส่วนตัวต้นทางเมื่อเข้าถึงรูท FileSystemDirectoryHandle
ได้
const root = await navigator.storage.getDirectory();
// Create a new file handle.
const fileHandle = await root.getFileHandle('Untitled.txt', { create: true });
// Create a new directory handle.
const dirHandle = await root.getDirectoryHandle('New Folder', { create: true });
// Recursively remove a directory.
await root.removeEntry('Old Stuff', { recursive: true });
การเข้าถึงไฟล์ที่เพิ่มประสิทธิภาพแล้วจากระบบไฟล์ส่วนตัวต้นทาง
ระบบไฟล์ส่วนตัวของต้นทางให้สิทธิ์เข้าถึงไฟล์ประเภทพิเศษซึ่งเพิ่มประสิทธิภาพให้สูง เช่น โดยการเสนอสิทธิ์เขียนเนื้อหาของไฟล์ในตำแหน่งเดิมและสิทธิ์เขียนเฉพาะ ใน Chromium 102 ขึ้นไป จะมีเมธอดเพิ่มเติมในระบบไฟล์ส่วนตัวของต้นทางเพื่อลดความซับซ้อนในการเข้าถึงไฟล์ นั่นคือ createSyncAccessHandle()
(สําหรับการดําเนินการอ่านและเขียนแบบซิงค์)
รายการนี้จะแสดงใน FileSystemFileHandle
แต่แสดงเฉพาะใน Web Worker
// (Read and write operations are synchronous,
// but obtaining the handle is asynchronous.)
// Synchronous access exclusively in Worker contexts.
const accessHandle = await fileHandle.createSyncAccessHandle();
const writtenBytes = accessHandle.write(buffer);
const readBytes = accessHandle.read(buffer, { at: 1 });
การใช้ Polyfill
คุณไม่สามารถโพลีไฟล์เมธอด File System Access API ได้ทั้งหมด
- เมธอด
showOpenFilePicker()
สามารถประมาณได้ด้วยองค์ประกอบ<input type="file">
- คุณสามารถจําลองเมธอด
showSaveFilePicker()
ด้วยองค์ประกอบ<a download="file_name">
ได้ แม้ว่าการดำเนินการนี้จะทริกเกอร์การดาวน์โหลดแบบเป็นโปรแกรมและไม่อนุญาตให้เขียนทับไฟล์ที่มีอยู่ - วิธีการ
showDirectoryPicker()
สามารถจําลองได้โดยใช้องค์ประกอบ<input type="file" webkitdirectory>
ที่ไม่เป็นไปตามมาตรฐาน
เราได้พัฒนาไลบรารีชื่อ browser-fs-access ที่ใช้ File System Access API ทุกครั้งที่เป็นไปได้ และจะใช้ตัวเลือกถัดไปที่ดีที่สุดในกรณีอื่นๆ ทั้งหมด
ความปลอดภัยและสิทธิ์
ทีม Chrome ได้ออกแบบและติดตั้งใช้งาน File System Access API โดยใช้หลักการหลักที่ระบุไว้ในการควบคุมการเข้าถึงฟีเจอร์ที่มีประสิทธิภาพของแพลตฟอร์มเว็บ ซึ่งรวมถึงการควบคุมและความโปร่งใสของผู้ใช้ รวมถึงความสะดวกสบายของผู้ใช้
การเปิดไฟล์หรือบันทึกไฟล์ใหม่
เมื่อเปิดไฟล์ ผู้ใช้จะให้สิทธิ์อ่านไฟล์หรือไดเรกทอรีโดยใช้เครื่องมือเลือกไฟล์
เครื่องมือเลือกไฟล์ที่เปิดอยู่จะแสดงได้โดยใช้ท่าทางสัมผัสของผู้ใช้เมื่อแสดงจากบริบทที่ปลอดภัยเท่านั้น หากผู้ใช้เปลี่ยนใจ ก็สามารถยกเลิกการเลือกในเครื่องมือเลือกไฟล์ได้ และเว็บไซต์จะไม่ได้รับสิทธิ์เข้าถึงข้อมูลใดๆ ซึ่งมีลักษณะการทำงานเหมือนกับองค์ประกอบ <input type="file">
ในทำนองเดียวกัน เมื่อเว็บแอปต้องการบันทึกไฟล์ใหม่ เบราว์เซอร์จะแสดงเครื่องมือเลือกไฟล์บันทึก ซึ่งช่วยให้ผู้ใช้ระบุชื่อและตำแหน่งของไฟล์ใหม่ได้ เนื่องจากผู้ใช้บันทึกไฟล์ใหม่ลงในอุปกรณ์ (ไม่ใช่การเขียนทับไฟล์ที่มีอยู่) เครื่องมือเลือกไฟล์จึงให้สิทธิ์แก่แอปในการเขียนลงในไฟล์
โฟลเดอร์ที่ถูกจำกัด
เบราว์เซอร์อาจจำกัดความสามารถในการบันทึกของผู้ใช้ไปยังโฟลเดอร์บางโฟลเดอร์ เช่น โฟลเดอร์หลักของระบบปฏิบัติการ เช่น Windows, โฟลเดอร์ไลบรารีของ macOS เพื่อช่วยปกป้องผู้ใช้และข้อมูลของผู้ใช้ เมื่อเกิดกรณีนี้ขึ้น เบราว์เซอร์จะแสดงข้อความแจ้งและขอให้ผู้ใช้เลือกโฟลเดอร์อื่น
การแก้ไขไฟล์หรือไดเรกทอรีที่มีอยู่
เว็บแอปจะแก้ไขไฟล์ในดิสก์ไม่ได้หากไม่ได้รับสิทธิ์อย่างชัดเจนจากผู้ใช้
ข้อความแจ้งสิทธิ์
หากผู้ใช้ต้องการบันทึกการเปลี่ยนแปลงในไฟล์ที่เคยให้สิทธิ์การอ่านไว้ก่อนหน้านี้ เบราว์เซอร์จะแสดงข้อความแจ้งสิทธิ์เพื่อขอสิทธิ์ให้เว็บไซต์เขียนการเปลี่ยนแปลงลงในดิสก์ คำขอสิทธิ์จะทริกเกอร์โดยท่าทางสัมผัสของผู้ใช้เท่านั้น เช่น การคลิกปุ่มบันทึก
หรือเว็บแอปที่แก้ไขไฟล์หลายไฟล์ เช่น IDE ก็ขอสิทธิ์บันทึกการเปลี่ยนแปลงได้เช่นกันเมื่อเปิดไฟล์
หากผู้ใช้เลือก "ยกเลิก" และไม่ให้สิทธิ์การเขียน เว็บแอปจะบันทึกการเปลี่ยนแปลงลงในไฟล์ในเครื่องไม่ได้ โดยควรระบุวิธีอื่นให้ผู้ใช้บันทึกข้อมูล เช่น ระบุวิธี"ดาวน์โหลด" ไฟล์หรือบันทึกข้อมูลลงในระบบคลาวด์
ความโปร่งใส
เมื่อผู้ใช้ให้สิทธิ์เว็บแอปในการบันทึกไฟล์ในเครื่อง เบราว์เซอร์จะแสดงไอคอนในแถบที่อยู่ การคลิกไอคอนจะเปิดป๊อปอัปที่แสดงรายการไฟล์ที่ผู้ใช้ให้สิทธิ์เข้าถึง ผู้ใช้สามารถเพิกถอนสิทธิ์เข้าถึงดังกล่าวได้ทุกเมื่อหากต้องการ
การเก็บรักษาสิทธิ์
เว็บแอปจะบันทึกการเปลี่ยนแปลงในไฟล์ต่อไปได้โดยไม่ต้องแจ้งเตือนจนกว่าคุณจะปิดแท็บทั้งหมดของแหล่งที่มา เมื่อปิดแท็บแล้ว เว็บไซต์จะเสียสิทธิ์เข้าถึงทั้งหมด เมื่อผู้ใช้ใช้เว็บแอปในครั้งถัดไป ระบบจะแจ้งให้ผู้ใช้เข้าถึงไฟล์อีกครั้ง
ความคิดเห็น
เราอยากทราบความคิดเห็นของคุณเกี่ยวกับ File System Access API
บอกเราเกี่ยวกับการออกแบบ API
มีสิ่งใดเกี่ยวกับ API ที่ไม่ทำงานตามที่คาดไว้ไหม หรือมีเมธอดหรือพร็อพเพอร์ตี้ที่ขาดหายไปซึ่งคุณต้องนำไปใช้กับแนวคิดของคุณ หากมีคำถามหรือความคิดเห็นเกี่ยวกับรูปแบบการรักษาความปลอดภัย
- แจ้งปัญหาเกี่ยวกับข้อกำหนดในที่เก็บ GitHub ของ WICG File System Access หรือแสดงความคิดเห็นในปัญหาที่มีอยู่
พบปัญหาในการติดตั้งใช้งานใช่ไหม
หากพบข้อบกพร่องในการใช้งาน Chrome หรือการติดตั้งใช้งานแตกต่างจากข้อมูลจำเพาะหรือไม่
- รายงานข้อบกพร่องที่ https://new.crbug.com โปรดระบุรายละเอียดให้มากที่สุดเท่าที่จะเป็นไปได้ รวมถึงวิธีการจำลองข้อบกพร่อง และตั้งค่าคอมโพเนนต์เป็น
Blink>Storage>FileSystem
Glitch เหมาะอย่างยิ่งสำหรับการแชร์การจำลองอย่างรวดเร็ว
หากมีแผนจะใช้ API
หากวางแผนที่จะใช้ File System Access API ในเว็บไซต์ การสนับสนุนแบบสาธารณะของคุณช่วยให้เราจัดลำดับความสำคัญของฟีเจอร์ต่างๆ ได้ และแสดงให้เห็นว่าการสนับสนุนฟีเจอร์เหล่านี้สำคัญกับผู้ให้บริการเบราว์เซอร์รายอื่นๆ เพียงใด
- แชร์ว่าคุณวางแผนจะใช้ WICG อย่างไรในชุดข้อความ Discourse ของ WICG
- ส่งทวีตถึง @ChromiumDev โดยใช้แฮชแท็ก
#FileSystemAccess
และบอกเราว่าคุณกำลังใช้ฟีเจอร์นี้ที่ไหนและอย่างไร
ลิงก์ที่มีประโยชน์
- คำอธิบายแบบสาธารณะ
- ข้อกำหนดการเข้าถึงระบบไฟล์และข้อกำหนดของไฟล์
- ข้อบกพร่องการติดตาม
- รายการ ChromeStatus.com
- คำจำกัดความของ TypeScript
- File System Access API - Chromium Security Model
- คอมโพเนนต์ Blink:
Blink>Storage>FileSystem
ขอขอบคุณ
Marijn Kruisselbrink เป็นผู้เขียนข้อกำหนดของ File System Access API