File System Access API ช่วยให้เว็บแอปอ่านหรือบันทึกการเปลี่ยนแปลงลงในไฟล์และโฟลเดอร์ในอุปกรณ์ของผู้ใช้โดยตรงได้
File System Access API คืออะไร
File System Access API ช่วยให้นักพัฒนาแอปสามารถสร้างเว็บแอปที่มีประสิทธิภาพซึ่งโต้ตอบกับไฟล์ในอุปกรณ์เครื่องของผู้ใช้ เช่น IDE, โปรแกรมแก้ไขรูปภาพและวิดีโอ, เครื่องมือแก้ไขข้อความ และอื่นๆ หลังจากผู้ใช้ให้สิทธิ์เข้าถึงเว็บแอปแล้ว API นี้จะอนุญาตให้เว็บแอปอ่านหรือบันทึกการเปลี่ยนแปลงลงในไฟล์และโฟลเดอร์ในอุปกรณ์ของผู้ใช้โดยตรง นอกจากการอ่านและเขียนไฟล์แล้ว File System Access API ยังมีความสามารถในการเปิดไดเรกทอรีและแสดงรายการเนื้อหา
หากคุณเคยอ่านและเขียนไฟล์มาก่อน ข้อมูลส่วนใหญ่ที่เราจะแชร์จะดูคุ้นเคย เราขอแนะนำให้คุณอ่านอยู่ดี เนื่องจากระบบแต่ละระบบอาจแตกต่างกัน
เบราว์เซอร์ Chromium ส่วนใหญ่ใน Windows, macOS, ChromeOS, Linux และ Android รองรับ File System Access API ข้อยกเว้นที่น่าสังเกตคือ Brave ซึ่งขณะนี้ใช้ได้เฉพาะหลัง Flag
การใช้ 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