ระบบไฟล์ส่วนตัวต้นทาง

File System Standard นำระบบไฟล์ส่วนตัวต้นทาง (OPFS) มาใช้เป็นปลายทางพื้นที่เก็บข้อมูลแบบส่วนตัวสำหรับต้นทางของหน้าเว็บ และจะไม่แสดงแก่ผู้ใช้ที่ให้สิทธิ์เข้าถึงไฟล์พิเศษประเภทพิเศษซึ่งมีการเพิ่มประสิทธิภาพที่สูง

การสนับสนุนเบราว์เซอร์

ระบบไฟล์ส่วนตัวต้นทางรองรับเบราว์เซอร์รุ่นใหม่และได้รับมาตรฐานจากคณะทำงานเทคโนโลยี Web Hypertext Application Technology (WHATWG) ใน File System Living Standard

การสนับสนุนเบราว์เซอร์

  • 86
  • 86
  • 111
  • 15.2

แหล่งที่มา

แรงจูงใจ

เมื่อนึกถึงไฟล์ในคอมพิวเตอร์ ให้คุณนึกถึงลำดับชั้นของไฟล์ ซึ่งหมายถึงไฟล์ที่จัดอยู่ในโฟลเดอร์ ซึ่งคุณสามารถสำรวจด้วย File Explorer ของระบบปฏิบัติการได้ ตัวอย่างเช่น ใน Windows สำหรับผู้ใช้ที่ชื่อ Tom รายการสิ่งที่ต้องทำอาจอยู่ใน C:\Users\Tom\Documents\ToDo.txt ในตัวอย่างนี้ ToDo.txt เป็นชื่อไฟล์ และ Users, Tom และ Documents เป็นชื่อโฟลเดอร์ "C:" ใน Windows หมายถึงไดเรกทอรีรากของไดรฟ์

วิธีดั้งเดิมในการทำงานกับไฟล์บนเว็บ

หากต้องการแก้ไขรายการสิ่งที่ต้องทำในเว็บแอปพลิเคชัน การดำเนินการตามขั้นตอนปกติได้แก่:

  1. ผู้ใช้อัปโหลดไฟล์ไปยังเซิร์ฟเวอร์หรือเปิดไฟล์ในไคลเอ็นต์ด้วย <input type="file">
  2. ผู้ใช้ทำการเปลี่ยนแปลง จากนั้นดาวน์โหลดไฟล์ผลลัพธ์ที่มี <a download="ToDo.txt> ที่แทรกไว้ซึ่งคุณใช้โปรแกรม click() ผ่าน JavaScript
  3. ในการเปิดโฟลเดอร์ คุณใช้แอตทริบิวต์พิเศษใน <input type="file" webkitdirectory> ซึ่งแม้จะมีชื่อที่เป็นกรรมสิทธิ์แต่รองรับเบราว์เซอร์ได้ทั่วโลกในทางปฏิบัติ

วิธีสมัยใหม่ในการทำงานกับไฟล์บนเว็บ

กระบวนการนี้ไม่ได้กล่าวถึงมุมมองที่ผู้ใช้คิดเกี่ยวกับการแก้ไขไฟล์ แต่หมายความว่าผู้ใช้ได้รับสำเนาไฟล์อินพุตที่ดาวน์โหลดมา ดังนั้น File System Access API จึงแนะนำวิธีเลือก 3 วิธี ได้แก่ showOpenFilePicker(), showSaveFilePicker() และ showDirectoryPicker() ซึ่งทำงานตรงตามชื่อที่แนะนำ โดยจะเปิดใช้ขั้นตอนดังนี้

  1. เปิด ToDo.txt ด้วย showOpenFilePicker() และรับออบเจ็กต์ FileSystemFileHandle
  2. จากออบเจ็กต์ FileSystemFileHandle ให้รับ File โดยเรียกใช้เมธอด getFile() ของแฮนเดิลไฟล์
  3. แก้ไขไฟล์ จากนั้นเรียกใช้ requestPermission({mode: 'readwrite'}) ที่แฮนเดิล
  4. หากผู้ใช้ยอมรับคำขอสิทธิ์ ให้บันทึกการเปลี่ยนแปลงกลับไปยังไฟล์ต้นฉบับ
  5. หรือโทรหา showSaveFilePicker() แล้วให้ผู้ใช้เลือกไฟล์ใหม่ (หากผู้ใช้เลือกไฟล์ที่เปิดไว้ก่อนหน้านี้ ระบบจะเขียนทับเนื้อหาในไฟล์) สำหรับการบันทึกซ้ำ คุณสามารถเก็บแฮนเดิลไฟล์ไว้ได้โดยไม่ต้องแสดงกล่องโต้ตอบการบันทึกไฟล์อีกครั้ง

ข้อจำกัดของการทำงานกับไฟล์บนเว็บ

ไฟล์และโฟลเดอร์ที่เข้าถึงได้ผ่านทางวิธีการเหล่านี้จะทำงานอยู่ในระบบไฟล์ที่ผู้ใช้มองเห็นได้ ไฟล์ที่บันทึกไว้จากเว็บและไฟล์ปฏิบัติการโดยเฉพาะจะมีเครื่องหมายของเว็บกำกับอยู่ ดังนั้นจึงมีคำเตือนเพิ่มเติมที่ระบบปฏิบัติการสามารถแสดงก่อนไฟล์ที่อาจเป็นอันตราย เนื่องจากเป็นฟีเจอร์ความปลอดภัยเพิ่มเติม ไฟล์ที่ได้จากเว็บจะได้รับการปกป้องโดย Google Safe Browsing ด้วย ซึ่งหมายความว่าคุณจะมองว่าเป็นการสแกนไวรัสในระบบคลาวด์ได้อย่างง่ายดาย และในบริบทของบทความนี้ เมื่อเขียนข้อมูลไปยังไฟล์โดยใช้ File System Access API การเขียนจะใช้งานไม่ได้ แต่จะใช้ไฟล์ชั่วคราว ตัวไฟล์จะไม่ได้รับการแก้ไขเว้นแต่จะผ่านการตรวจสอบความปลอดภัยเหล่านี้ทั้งหมด คุณคงพอจะเข้าใจว่าการดำเนินการนี้จะทำให้ไฟล์ทำงานค่อนข้างช้า แม้จะมีการปรับปรุงเกิดขึ้นหากเป็นไปได้ เช่น ใน macOS อย่างไรก็ตาม การเรียก write() ทุกครั้งจะทำงานด้วยตัวเอง ดังนั้นภายในขั้นสูง ระบบจะเปิดไฟล์ ค้นหาออฟเซ็ตที่ระบุ และเขียนข้อมูลในที่สุด

การใช้ไฟล์เป็นพื้นฐานของการประมวลผล

ในขณะเดียวกัน ไฟล์ก็เป็นวิธีที่ยอดเยี่ยมในการบันทึกข้อมูล ตัวอย่างเช่น SQLite จะจัดเก็บฐานข้อมูลทั้งหมดไว้ในไฟล์เดียว อีกตัวอย่างหนึ่งคือ mipmaps ที่ใช้ในการประมวลผลรูปภาพ Mipmaps จะคำนวณตามลำดับและเพิ่มประสิทธิภาพของภาพ ซึ่งแต่ละภาพจะแสดงความละเอียดต่ำลงเรื่อยๆ ทำให้การดำเนินการหลายอย่าง เช่น การซูมเร็วขึ้น แล้วเว็บแอปพลิเคชันจะได้รับประโยชน์ของไฟล์ แต่ไม่มีค่าใช้จ่ายด้านประสิทธิภาพในการประมวลผลไฟล์บนเว็บได้อย่างไร คำตอบคือระบบไฟล์ส่วนตัวต้นทาง

ระบบไฟล์ส่วนตัวของต้นทางที่ผู้ใช้มองเห็นได้

ระบบไฟล์ส่วนตัวต้นทางไม่แสดงต่อผู้ใช้ ต่างจากระบบไฟล์ที่ผู้ใช้มองเห็นได้ซึ่งเรียกดูโดยใช้ File Explorer ของระบบปฏิบัติการ ไฟล์และโฟลเดอร์ในระบบไฟล์ส่วนตัวต้นทางตามชื่อที่แนะนำ เป็นแบบส่วนตัวและเป็นส่วนตัวมากกว่าสำหรับต้นทางของเว็บไซต์ สำรวจต้นทางของหน้าเว็บโดยพิมพ์ location.origin ในคอนโซลเครื่องมือสำหรับนักพัฒนาเว็บ เช่น จุดเริ่มต้นของหน้า https://developer.chrome.com/articles/ คือ https://developer.chrome.com (กล่าวคือ ส่วน /articles ไม่ใช่ส่วนของต้นทาง) อ่านข้อมูลเพิ่มเติมเกี่ยวกับทฤษฎีต้นทางได้ในการทำความเข้าใจ "เว็บไซต์เดียวกัน" และ "ต้นทางเดียวกัน" หน้าทั้งหมดที่แชร์ต้นทางเดียวกันจะเห็นข้อมูลระบบไฟล์ส่วนตัวของต้นทางเดียวกัน ดังนั้น https://developer.chrome.com/docs/extensions/mv3/getstarted/extensions-101/ จะเห็นรายละเอียดเดียวกันกับตัวอย่างก่อนหน้านี้ แต่ละต้นทางมีระบบไฟล์ส่วนตัวของต้นทางเป็นอิสระจากของตัวเอง ซึ่งหมายความว่าระบบไฟล์ส่วนตัวของต้นทาง https://developer.chrome.com จะแตกต่างจาก https://web.dev อย่างสิ้นเชิง ใน Windows ไดเรกทอรีรากของระบบไฟล์ที่ผู้ใช้มองเห็นคือ C:\\ ค่าที่เทียบเท่าสำหรับระบบไฟล์ส่วนตัวของต้นทางคือไดเรกทอรีรูทที่ว่างเปล่าในช่วงแรกต่อต้นทางที่เข้าถึงโดยการเรียกใช้เมธอดแบบไม่พร้อมกัน navigator.storage.getDirectory() โปรดดูแผนภาพต่อไปนี้ในการเปรียบเทียบระบบไฟล์ที่ผู้ใช้มองเห็นและระบบไฟล์ส่วนตัวต้นทาง แผนภาพแสดงให้เห็นว่านอกเหนือจากไดเรกทอรีรากแล้ว ปัจจัยอื่นๆ ทั้งหมดก็มีแนวคิดเหมือนกัน โดยมีลำดับชั้นของไฟล์และโฟลเดอร์เพื่อจัดระเบียบและจัดเรียงตามความต้องการของข้อมูลและพื้นที่เก็บข้อมูล

แผนภาพของระบบไฟล์ที่ผู้ใช้มองเห็นและระบบไฟล์ส่วนตัวต้นทางที่มีลำดับชั้นไฟล์เป็นข้อยกเว้น 2 ลำดับชั้น จุดแรกเข้าสำหรับระบบไฟล์ที่ผู้ใช้มองเห็นได้คือฮาร์ดดิสก์สัญลักษณ์ ซึ่งจุดแรกเข้าสำหรับระบบไฟล์ส่วนตัวต้นทางจะเรียกเมธอด &#39;navigator.storage.getDirectory&#39;

ข้อมูลจำเพาะเกี่ยวกับระบบไฟล์ส่วนตัวต้นทาง

ระบบไฟล์ส่วนตัวต้นทางขึ้นอยู่กับข้อจำกัดโควต้าของเบราว์เซอร์ เช่นเดียวกับกลไกพื้นที่เก็บข้อมูลอื่นๆ ในเบราว์เซอร์ (เช่น localStorage หรือ IndexedDB) เมื่อผู้ใช้ล้างข้อมูลการท่องเว็บทั้งหมดหรือข้อมูลเว็บไซต์ทั้งหมด ระบบจะลบระบบไฟล์ส่วนตัวต้นทางออกด้วย เรียกใช้ navigator.storage.estimate() และในออบเจ็กต์คำตอบที่เป็นผลลัพธ์ คุณจะเห็นรายการ usage เพื่อดูปริมาณพื้นที่เก็บข้อมูลที่แอปใช้ไปแล้ว ซึ่งจะแยกตามกลไกพื้นที่เก็บข้อมูลในออบเจ็กต์ usageDetails ซึ่งคุณต้องการดูรายการ fileSystem โดยเฉพาะ เนื่องจากระบบไฟล์ส่วนตัวต้นทางจะมองไม่เห็นผู้ใช้ จึงไม่มีข้อความแจ้งเกี่ยวกับสิทธิ์และไม่มีการตรวจสอบ Google Safe Browsing

การเข้าถึงไดเรกทอรีราก

หากต้องการเข้าถึงไดเรกทอรีราก ให้เรียกใช้คำสั่งต่อไปนี้ สุดท้ายแล้วคุณจะเห็นแฮนเดิลไดเรกทอรีที่ว่างเปล่า โดยเฉพาะ FileSystemDirectoryHandle

const opfsRoot = await navigator.storage.getDirectory();
// A FileSystemDirectoryHandle whose type is "directory"
// and whose name is "".
console.log(opfsRoot);

เทรดหลักหรือ Web Worker

การใช้ระบบไฟล์ส่วนตัวของต้นทางมี 2 วิธี ได้แก่ ในเทรดหลักหรือใน Web Worker Web Workers ไม่สามารถบล็อกเทรดหลักได้ ซึ่งหมายความว่า API ของบริบทนี้สามารถเป็นแบบพร้อมกัน ซึ่งเป็นรูปแบบที่โดยทั่วไปแล้วจะไม่ได้รับอนุญาตในเทรดหลัก API แบบซิงโครนัสทำงานได้เร็วขึ้นเนื่องจากไม่จำเป็นต้องจัดการกับสัญญา และการดำเนินการไฟล์มักทำพร้อมกันในภาษาเช่น C ซึ่งสามารถคอมไพล์ไปยัง WebAssembly

// This is synchronous C code.
FILE *f;
f = fopen("example.txt", "w+");
fputs("Some text\n", f);
fclose(f);

หากต้องการให้ดำเนินการกับไฟล์ได้เร็วที่สุดหรือกำลังใช้ WebAssembly ให้ข้ามไปที่หัวข้อใช้ระบบไฟล์ส่วนตัวต้นทางใน Web Worker ซึ่งคุณอ่านต่อได้

ใช้ระบบไฟล์ส่วนตัวของต้นทางบนเทรดหลัก

สร้างไฟล์และโฟลเดอร์ใหม่

เมื่อมีโฟลเดอร์รูทแล้ว ให้สร้างไฟล์และโฟลเดอร์โดยใช้เมธอด getFileHandle() และ getDirectoryHandle() ตามลำดับ เมื่อส่งผ่าน {create: true} ระบบจะสร้างไฟล์หรือโฟลเดอร์หากยังไม่มี สร้างลำดับชั้นของไฟล์โดยเรียกใช้ฟังก์ชันเหล่านี้โดยใช้ไดเรกทอรีที่สร้างใหม่เป็นจุดเริ่มต้น

const fileHandle = await opfsRoot
    .getFileHandle('my first file', {create: true});
const directoryHandle = await opfsRoot
    .getDirectoryHandle('my first folder', {create: true});
const nestedFileHandle = await directoryHandle
    .getFileHandle('my first nested file', {create: true});
const nestedDirectoryHandle = await directoryHandle
    .getDirectoryHandle('my first nested folder', {create: true});

ลำดับชั้นของไฟล์ที่ได้จากตัวอย่างโค้ดก่อนหน้านี้

เข้าถึงไฟล์และโฟลเดอร์ที่มีอยู่

หากคุณทราบชื่อของบุคคลดังกล่าว ให้เข้าถึงไฟล์และโฟลเดอร์ที่สร้างไว้ก่อนหน้านี้โดยเรียกใช้เมธอด getFileHandle() หรือ getDirectoryHandle() แล้วส่งผ่านชื่อไฟล์หรือโฟลเดอร์

const existingFileHandle = await opfsRoot.getFileHandle('my first file');
const existingDirectoryHandle = await opfsRoot
    .getDirectoryHandle('my first folder');

การรับไฟล์ที่เชื่อมโยงกับแฮนเดิลไฟล์สำหรับการอ่าน

FileSystemFileHandle หมายถึงไฟล์ในระบบไฟล์ หากต้องการรับ File ที่เกี่ยวข้อง ให้ใช้เมธอด getFile() ออบเจ็กต์ File เป็น Blob ประเภทหนึ่ง และใช้ได้ในบริบทใดก็ได้ที่ Blob ทำได้ โดยเฉพาะอย่างยิ่ง FileReader, URL.createObjectURL(), createImageBitmap() และ XMLHttpRequest.send() ยอมรับทั้ง Blobs และ Files หากดำเนินการดังกล่าว จะรับ File จาก FileSystemFileHandle เพื่อ "คืนค่า" ข้อมูล เพื่อให้คุณเข้าถึงและทำให้ใช้งานได้ในระบบไฟล์ที่ผู้ใช้มองเห็น

const file = await fileHandle.getFile();
console.log(await file.text());

เขียนลงไฟล์ด้วยการสตรีม

สตรีมข้อมูลไปยังไฟล์โดยเรียกใช้ createWritable() ซึ่งจะสร้าง FileSystemWritableFileStream แล้วให้คุณwrite() เนื้อหา ในตอนท้าย คุณต้องclose()สตรีม

const contents = 'Some text';
// Get a writable stream.
const writable = await fileHandle.createWritable();
// Write the contents of the file to the stream.
await writable.write(contents);
// Close the stream, which persists the contents.
await writable.close();

ลบไฟล์และโฟลเดอร์

ลบไฟล์และโฟลเดอร์โดยเรียกใช้เมธอด remove() เฉพาะของไฟล์หรือโฟลเดอร์ในไดเรกทอรี หากต้องการลบโฟลเดอร์ที่มีโฟลเดอร์ย่อยทั้งหมด ให้ส่งตัวเลือก {recursive: true}

await fileHandle.remove();
await directoryHandle.remove({recursive: true});

หรือหากคุณทราบชื่อของไฟล์หรือโฟลเดอร์ที่จะลบในไดเรกทอรี ให้ใช้เมธอด removeEntry()

directoryHandle.removeEntry('my first nested file');

ย้ายและเปลี่ยนชื่อไฟล์และโฟลเดอร์

เปลี่ยนชื่อและย้ายไฟล์และโฟลเดอร์โดยใช้เมธอด move() การย้ายและเปลี่ยนชื่อสามารถเกิดขึ้นพร้อมกันหรือแยกกันก็ได้

// Rename a file.
await fileHandle.move('my first renamed file');
// Move a file to another directory.
await fileHandle.move(nestedDirectoryHandle);
// Move a file to another directory and rename it.
await fileHandle
    .move(nestedDirectoryHandle, 'my first renamed and now nested file');

ปิดเส้นทางของไฟล์หรือโฟลเดอร์

หากต้องการทราบตำแหน่งของไฟล์หรือโฟลเดอร์ที่ระบุซึ่งเกี่ยวข้องกับไดเรกทอรีอ้างอิง ให้ใช้เมธอด resolve() โดยส่งผ่าน FileSystemHandle เป็นอาร์กิวเมนต์ หากต้องการดูเส้นทางแบบเต็มของไฟล์หรือโฟลเดอร์ในระบบไฟล์ส่วนตัวต้นทาง ให้ใช้ไดเรกทอรีรากเป็นไดเรกทอรีอ้างอิงที่ได้จาก navigator.storage.getDirectory()

const relativePath = await opfsRoot.resolve(nestedDirectoryHandle);
// `relativePath` is `['my first folder', 'my first nested folder']`.

ตรวจสอบว่าแฮนเดิลไฟล์หรือโฟลเดอร์ 2 รายการชี้ไปที่ไฟล์หรือโฟลเดอร์เดียวกันหรือไม่

บางครั้งจะมีแฮนเดิล 2 อันและไม่ทราบว่าแฮนเดิลชี้ไปที่ไฟล์หรือโฟลเดอร์เดียวกันหรือไม่ หากต้องการตรวจสอบว่าเป็นกรณีนี้หรือไม่ ให้ใช้เมธอด isSameEntry()

fileHandle.isSameEntry(nestedFileHandle);
// Returns `false`.

แสดงเนื้อหาของโฟลเดอร์

FileSystemDirectoryHandle เป็นตัวทำซ้ำแบบไม่พร้อมกันที่คุณทำซ้ำได้ด้วยการวนซ้ำ for await…of ในฐานะที่เป็นตัวกระตุ้นแบบอะซิงโครนัส เครื่องมือนี้ยังรองรับเมธอด entries(), values() และ keys() ซึ่งคุณสามารถเลือกใช้ได้โดยขึ้นอยู่กับข้อมูลที่ต้องการ ดังนี้

for await (let [name, handle] of directoryHandle) {}
for await (let [name, handle] of directoryHandle.entries()) {}
for await (let handle of directoryHandle.values()) {}
for await (let name of directoryHandle.keys()) {}

แสดงเนื้อหาของโฟลเดอร์และโฟลเดอร์ย่อยทั้งหมดซ้ำๆ

การจัดการการลูปและฟังก์ชันแบบอะซิงโครนัสที่จับคู่กับการทำซ้ำนั้นไม่ใช่เรื่องง่าย ฟังก์ชันด้านล่างสามารถใช้เป็นจุดเริ่มต้นสำหรับการแสดงเนื้อหาของโฟลเดอร์และโฟลเดอร์ย่อยทั้งหมด รวมถึงไฟล์และขนาดต่างๆ ในโฟลเดอร์ คุณสามารถลดความซับซ้อนของฟังก์ชันได้ถ้าไม่ต้องการให้ไฟล์มีขนาดตามที่ระบุ directoryEntryPromises.push ไม่ต้องผลักดันhandle.getFile() แต่ให้ระบุhandleโดยตรง

  const getDirectoryEntriesRecursive = async (
    directoryHandle,
    relativePath = '.',
  ) => {
    const fileHandles = [];
    const directoryHandles = [];
    const entries = {};
    // Get an iterator of the files and folders in the directory.
    const directoryIterator = directoryHandle.values();
    const directoryEntryPromises = [];
    for await (const handle of directoryIterator) {
      const nestedPath = `${relativePath}/${handle.name}`;
      if (handle.kind === 'file') {
        fileHandles.push({ handle, nestedPath });
        directoryEntryPromises.push(
          handle.getFile().then((file) => {
            return {
              name: handle.name,
              kind: handle.kind,
              size: file.size,
              type: file.type,
              lastModified: file.lastModified,
              relativePath: nestedPath,
              handle
            };
          }),
        );
      } else if (handle.kind === 'directory') {
        directoryHandles.push({ handle, nestedPath });
        directoryEntryPromises.push(
          (async () => {
            return {
              name: handle.name,
              kind: handle.kind,
              relativePath: nestedPath,
              entries:
                  await getDirectoryEntriesRecursive(handle, nestedPath),
              handle,
            };
          })(),
        );
      }
    }
    const directoryEntries = await Promise.all(directoryEntryPromises);
    directoryEntries.forEach((directoryEntry) => {
      entries[directoryEntry.name] = directoryEntry;
    });
    return entries;
  };

ใช้ระบบไฟล์ส่วนตัวของต้นทางใน Web Worker

ตามที่ระบุไว้ก่อนหน้านี้ Web Workers ไม่สามารถบล็อกเทรดหลักได้ ซึ่งเป็นเหตุผลที่ทำให้เมธอดแบบซิงโครนัสนี้ได้รับอนุญาต

การรับแฮนเดิลการเข้าถึงแบบซิงโครนัส

จุดแรกเข้าในการใช้งานไฟล์ที่เร็วที่สุดคือ FileSystemSyncAccessHandle ซึ่งได้รับมาจาก FileSystemFileHandle ปกติโดยการเรียกใช้ createSyncAccessHandle()

const fileHandle = await opfsRoot
    .getFileHandle('my highspeed file.txt', {create: true});
const syncAccessHandle = await fileHandle.createSyncAccessHandle();

วิธีการเข้าถึงไฟล์ในตำแหน่งแบบซิงโครนัส

เมื่อมีแฮนเดิลการเข้าถึงแบบซิงโครนัสแล้ว คุณจะเข้าถึงเมธอดไฟล์ในตำแหน่งที่ทำงานพร้อมกันได้อย่างรวดเร็ว

  • getSize(): แสดงผลขนาดไฟล์เป็นไบต์
  • write(): เขียนเนื้อหาของบัฟเฟอร์ลงในไฟล์ เลือกที่ออฟเซ็ตที่กำหนด และแสดงผลจำนวนไบต์ที่เขียน การตรวจสอบจำนวนไบต์ที่เขียนกลับคืนมาจะช่วยให้ผู้โทรตรวจหาและจัดการข้อผิดพลาดและการเขียนบางส่วนได้
  • read(): อ่านเนื้อหาของไฟล์ลงในบัฟเฟอร์ (ไม่บังคับ) ตามออฟเซ็ตที่ระบุ
  • truncate(): ปรับขนาดไฟล์เป็นขนาดที่ระบุ
  • flush(): ตรวจสอบว่าเนื้อหาของไฟล์มีการแก้ไขทั้งหมดที่ดำเนินการผ่าน write()
  • close(): ปิดแฮนเดิลการเข้าถึง

นี่คือตัวอย่างที่ใช้วิธีการทั้งหมดที่กล่าวถึงข้างต้น

const opfsRoot = await navigator.storage.getDirectory();
const fileHandle = await opfsRoot.getFileHandle('fast', {create: true});
const accessHandle = await fileHandle.createSyncAccessHandle();

const textEncoder = new TextEncoder();
const textDecoder = new TextDecoder();

// Initialize this variable for the size of the file.
let size;
// The current size of the file, initially `0`.
size = accessHandle.getSize();
// Encode content to write to the file.
const content = textEncoder.encode('Some text');
// Write the content at the beginning of the file.
accessHandle.write(content, {at: size});
// Flush the changes.
accessHandle.flush();
// The current size of the file, now `9` (the length of "Some text").
size = accessHandle.getSize();

// Encode more content to write to the file.
const moreContent = textEncoder.encode('More content');
// Write the content at the end of the file.
accessHandle.write(moreContent, {at: size});
// Flush the changes.
accessHandle.flush();
// The current size of the file, now `21` (the length of
// "Some textMore content").
size = accessHandle.getSize();

// Prepare a data view of the length of the file.
const dataView = new DataView(new ArrayBuffer(size));

// Read the entire file into the data view.
accessHandle.read(dataView);
// Logs `"Some textMore content"`.
console.log(textDecoder.decode(dataView));

// Read starting at offset 9 into the data view.
accessHandle.read(dataView, {at: 9});
// Logs `"More content"`.
console.log(textDecoder.decode(dataView));

// Truncate the file after 4 bytes.
accessHandle.truncate(4);

คัดลอกไฟล์จากระบบไฟล์ส่วนตัวของต้นทางไปยังระบบไฟล์ที่ผู้ใช้มองเห็นได้

ดังที่กล่าวไว้ข้างต้น คุณจะย้ายไฟล์จากระบบไฟล์ส่วนตัวต้นทางไปยังระบบไฟล์ที่ผู้ใช้มองเห็นไม่ได้ แต่จะคัดลอกไฟล์ได้ เนื่องจาก showSaveFilePicker() จะแสดงเฉพาะในเทรดหลักเท่านั้น แต่ไม่แสดงในเทรดผู้ปฏิบัติงาน โปรดเรียกใช้โค้ดในนั้น

// On the main thread, not in the Worker. This assumes
// `fileHandle` is the `FileSystemFileHandle` you obtained
// the `FileSystemSyncAccessHandle` from in the Worker
// thread. Be sure to close the file in the Worker thread first.
const fileHandle = await opfsRoot.getFileHandle('fast');
try {
  // Obtain a file handle to a new file in the user-visible file system
  // with the same name as the file in the origin private file system.
  const saveHandle = await showSaveFilePicker({
    suggestedName: fileHandle.name || ''
  });
  const writable = await saveHandle.createWritable();
  await writable.write(await fileHandle.getFile());
  await writable.close();
} catch (err) {
  console.error(err.name, err.message);
}

แก้ไขข้อบกพร่องระบบไฟล์ส่วนตัวของต้นทาง

จนกว่าจะมีการเพิ่มการสนับสนุนเครื่องมือสำหรับนักพัฒนาเว็บในตัว (ดูที่ crbug/1284595) ให้ใช้ส่วนขยาย Chrome OPFS Explorer เพื่อแก้ไขข้อบกพร่องของระบบไฟล์ส่วนตัวของต้นทาง ภาพหน้าจอด้านบนจากส่วนการสร้างไฟล์และโฟลเดอร์ใหม่จะมาจากส่วนขยายโดยตรง

ส่วนขยาย Chrome DevTools ของ OPFS Explorer ใน Chrome เว็บสโตร์

หลังจากติดตั้งส่วนขยายแล้ว ให้เปิดเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome เลือกแท็บ OPFS Explorer จากนั้นคุณจะพร้อมตรวจสอบลำดับชั้นของไฟล์ บันทึกไฟล์จากระบบไฟล์ส่วนตัวของต้นทางไปยังระบบไฟล์ที่ผู้ใช้มองเห็นได้ โดยคลิกชื่อไฟล์และลบไฟล์และโฟลเดอร์โดยคลิกไอคอนถังขยะ

ข้อมูลประชากร

ดูการใช้งานระบบไฟล์ส่วนตัวต้นทาง (หากคุณติดตั้งส่วนขยาย OPFS Explorer) ในการสาธิตที่ใช้เป็นแบ็กเอนด์สำหรับฐานข้อมูล SQLite ที่คอมไพล์ไปยัง WebAssembly อย่าลืมดูซอร์สโค้ดใน Glitch โปรดทราบว่าเวอร์ชันที่ฝังอยู่ด้านล่างไม่ได้ใช้แบ็กเอนด์ของระบบไฟล์ส่วนตัวต้นทาง (เนื่องจาก iframe เป็นแบบข้ามต้นทาง) แต่เมื่อคุณเปิดการสาธิตในแท็บแยกต่างหาก ก็จะใช้

บทสรุป

ระบบไฟล์ส่วนตัวต้นทางตามที่ระบุไว้โดย WHATWG ได้เปลี่ยนวิถีชีวิตที่เราใช้และโต้ตอบกับไฟล์บนเว็บ การทำเช่นนี้ช่วยเปิดโอกาสให้ใช้ระบบไฟล์ที่ผู้ใช้มองเห็นได้ ซึ่งไม่สามารถทำได้จริง ผู้ให้บริการเบราว์เซอร์รายใหญ่ๆ ทั้งหมด ไม่ว่าจะเป็น Apple, Mozilla และ Google ก็ร่วมงานกับเราและต่างมีวิสัยทัศน์ร่วมกัน การพัฒนาระบบไฟล์ส่วนตัวของต้นทางเป็นความร่วมมือกันเป็นอย่างมาก ส่วนความคิดเห็นจากนักพัฒนาแอปและผู้ใช้ก็มีความสำคัญต่อความก้าวหน้าของการพัฒนาดังกล่าว ขณะที่เราปรับแต่งและปรับปรุงมาตรฐานอย่างต่อเนื่อง เรายินดีรับฟังความคิดเห็นเกี่ยวกับที่เก็บ whatwg/fs ในรูปแบบ "ปัญหา" หรือ "คำขอแบบพุล"

ข้อความแสดงการยอมรับ

บทความนี้ได้รับการตรวจสอบโดย Austin Sully, Etienne Noël และ Rachel Andrew รูปภาพหลักของ Christina Rumpf ใน Unsplash