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;
}
การขอสิทธิ์เขียนในคำขออ่านทำให้ฉันลดจำนวนข้อความแจ้งสิทธิ์ ผู้ใช้เห็นข้อความแจ้ง 1 รายการเมื่อเปิดไฟล์ และให้สิทธิ์ทั้งการอ่านและเขียนลงในไฟล์
การเปิดไดเรกทอรีและแจกแจงเนื้อหา
หากต้องการแจกแจงไฟล์ทั้งหมดในไดเรกทอรี ให้เรียกใช้ 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 หรือเพิ่มความเห็นเกี่ยวกับปัญหาที่มีอยู่
พบปัญหาในการติดตั้งใช้งานใช่ไหม
หากพบข้อบกพร่องในการใช้งาน Chrome หรือการติดตั้งใช้งานแตกต่างจากข้อมูลจำเพาะหรือไม่
- รายงานข้อบกพร่องที่ https://new.crbug.com ตรวจสอบว่าได้ใส่รายละเอียดให้มากที่สุดเท่าที่จะทำได้ วิธีการทำซ้ำ และตั้งค่าคอมโพเนนต์เป็น
Blink>Storage>FileSystem
ภาพ Glitch เหมาะสำหรับการแชร์ภาพซ้ำอย่างรวดเร็ว
หากกำลังวางแผนที่จะใช้ API
หากวางแผนที่จะใช้ File System Access API ในเว็บไซต์ การสนับสนุนสาธารณะของคุณช่วยให้เราจัดลำดับความสำคัญของคุณลักษณะต่างๆ และแสดงให้ผู้ให้บริการเบราว์เซอร์รายอื่นเห็นว่าการสนับสนุนเบราว์เซอร์นั้นสำคัญเพียงใด
- แชร์วิธีที่คุณวางแผนจะใช้ในชุดข้อความของ WICG Discourse
- ส่งทวีตถึง @ChromiumDev โดยใช้แฮชแท็ก
#FileSystemAccess
และบอกเราว่าคุณกำลังใช้ฟีเจอร์นี้ที่ไหนและอย่างไร
ลิงก์ที่มีประโยชน์
- คำอธิบายแบบสาธารณะ
- ข้อกำหนดการเข้าถึงระบบไฟล์และข้อกำหนดของไฟล์
- ข้อบกพร่องการติดตาม
- รายการ ChromeStatus.com
- คำจำกัดความ TypeScript
- File System Access API - Chromium Security Model
- คอมโพเนนต์ Blink:
Blink>Storage>FileSystem
ขอขอบคุณ
ข้อกำหนดของ File System Access API เขียนโดย Marijn Kruisselbrink