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

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

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

ระบบไฟล์ส่วนตัวของต้นทางได้รับการรองรับโดยเบราว์เซอร์สมัยใหม่และได้รับการ กำหนดมาตรฐานโดย Web Hypertext Application Technology Working Group (WHATWG) ใน มาตรฐานระบบไฟล์แบบต่อเนื่อง

Browser Support

  • Chrome: 86.
  • Edge: 86.
  • Firefox: 111.
  • Safari: 15.2.

Source

แรงจูงใจ

เมื่อนึกถึงไฟล์ในคอมพิวเตอร์ คุณอาจนึกถึงลำดับชั้นของไฟล์ ซึ่งเป็นไฟล์ที่จัดระเบียบไว้ในโฟลเดอร์ที่คุณสำรวจได้ด้วย 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 รายการ ได้แก่

ซึ่งจะเปิดใช้ขั้นตอนดังนี้

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

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

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

การดำเนินการนี้ทำให้การดำเนินการกับไฟล์ค่อนข้างช้า แม้ว่าจะมีการปรับปรุงในจุดที่ทำได้ เช่น ใน macOS แต่การเรียกใช้ write() แต่ละครั้งจะทำงานด้วยตัวเอง ดังนั้นเบื้องหลังระบบจะเปิดไฟล์ ค้นหาออฟเซ็ตที่ระบุ และเขียนข้อมูลในที่สุด

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

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

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

ระบบไฟล์ส่วนตัวของต้นทางไม่ได้มีไว้ให้ผู้ใช้เห็น ซึ่งแตกต่างจากระบบไฟล์ที่ผู้ใช้มองเห็นได้ซึ่งเรียกดูได้โดยใช้ File Explorer ของระบบปฏิบัติการ และคุณสามารถอ่าน เขียน ย้าย และเปลี่ยนชื่อไฟล์และโฟลเดอร์ได้ ไฟล์และโฟลเดอร์ ในระบบไฟล์ส่วนตัวของต้นทางจะเป็นส่วนตัวตามชื่อที่ระบุไว้ และเป็นส่วนตัวสำหรับต้นทางของเว็บไซต์ และเป็นส่วนตัวสำหรับ ต้นทาง ของเว็บไซต์ ค้นหาต้นทางของหน้าเว็บโดยพิมพ์ location.origin ในคอนโซลเครื่องมือสำหรับนักพัฒนาเว็บ เช่น ต้นทางของหน้าเว็บ https://developer.chrome.com/articles/ คือ https://developer.chrome.com อ่านเพิ่มเติมเกี่ยวกับทฤษฎีของต้นทางใน หัวข้อทำความเข้าใจ "เว็บไซต์เดียวกัน" และ "ต้นทางเดียวกัน"

ทุกหน้าเว็บที่แชร์ต้นทางเดียวกันจะเห็นข้อมูลระบบไฟล์ส่วนตัวของต้นทางเดียวกัน ดังนั้น https://developer.chrome.com/docs/extensions/mv3/getstarted/extensions-101/ จะเห็นรายละเอียดเดียวกันกับตัวอย่างก่อนหน้า แต่ละต้นทางจะมีระบบไฟล์ส่วนตัวของต้นทางที่เป็นอิสระของตัวเอง ซึ่งหมายความว่าระบบไฟล์ส่วนตัวของต้นทางของ https://developer.chrome.com จะแตกต่างจากระบบไฟล์ส่วนตัวของต้นทางของ https://web.dev อย่างสิ้นเชิง ใน Windows ไดเรกทอรีรากของระบบไฟล์ที่ผู้ใช้มองเห็นได้คือ C:\\

ส่วนระบบไฟล์ส่วนตัวของต้นทางจะมีไดเรกทอรี รากที่ว่างเปล่าในตอนแรกต่อต้นทาง ซึ่งเข้าถึงได้โดยการเรียกใช้เมธอดแบบอะซิงโครนัส navigator.storage.getDirectory()

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

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

รายละเอียดของระบบไฟล์ส่วนตัวของต้นทาง

ระบบไฟล์ส่วนตัวของต้นทางอยู่ภายใต้ข้อจำกัดด้านโควต้าของเบราว์เซอร์ เช่นเดียวกับกลไกการจัดเก็บข้อมูลอื่นๆ ในเบราว์เซอร์ (เช่น 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 Worker จะบล็อกเทรดหลักไม่ได้ ซึ่งหมายความว่าในบริบทนี้ API อาจเป็นแบบซิงโครนัส ซึ่งโดยทั่วไปแล้วไม่อนุญาตในเทรดหลัก API แบบซิงโครนัสอาจทำงานได้เร็วกว่าเนื่องจากไม่ต้องจัดการกับ Promise และการดำเนินการกับไฟล์มักจะเป็นแบบซิงโครนัสในภาษาต่างๆ เช่น 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() Promise แต่ให้ส่ง 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 Worker จะบล็อกเทรดหลักไม่ได้ ซึ่งเป็นเหตุผลที่อนุญาตให้ใช้เมธอดแบบซิงโครนัสในบริบทนี้

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

จุดเริ่มต้นของการดำเนินการกับไฟล์ที่เร็วที่สุดเท่าที่จะเป็นไปได้คือ 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() แสดงเฉพาะในเทรดหลักเท่านั้น ไม่ได้แสดงในเทรด Worker โปรดตรวจสอบว่าได้เรียกใช้โค้ดในเทรดหลัก

// 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 เพื่อแก้ไขข้อบกพร่องระบบไฟล์ส่วนตัวของต้นทาง ภาพหน้าจอจาก ส่วน การสร้างไฟล์และโฟลเดอร์ใหม่ ได้ มาจากส่วนขยายโดยตรง

ส่วนขยาย OPFS Explorer สำหรับเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome ใน Chrome เว็บสโตร์

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

สาธิต

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

บทสรุป

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

ในขณะที่เราปรับแต่งและปรับปรุงมาตรฐานอย่างต่อเนื่อง เรายินดีรับความคิดเห็นเกี่ยวกับที่เก็บ whatwg/fs ในรูปแบบของปัญหาหรือ คำขอผสานการเปลี่ยนแปลง

คำขอบคุณ

เอกสารนี้ได้รับการตรวจสอบโดย Austin Sully, Etienne Noël และ Rachel Andrew