File System Standard นำระบบไฟล์ส่วนตัว (OPFS) จากต้นทางเป็นปลายทางพื้นที่เก็บข้อมูลส่วนตัวสำหรับต้นทางของหน้าเว็บ และไม่แสดงต่อผู้ใช้ที่ให้สิทธิ์เสริมการเข้าถึงไฟล์ชนิดพิเศษที่ได้รับการเพิ่มประสิทธิภาพอย่างมาก
การสนับสนุนเบราว์เซอร์
ระบบไฟล์ส่วนตัวที่เป็นต้นทางนั้นได้รับการสนับสนุนจากเบราว์เซอร์สมัยใหม่และได้รับมาตรฐานจากคณะทำงานเทคโนโลยีแอปพลิเคชันเว็บ Hypertext (WHATWG) ใน File System Living Standard
แรงจูงใจ
เมื่อนึกถึงไฟล์ในคอมพิวเตอร์ คุณอาจนึกถึงลำดับชั้นของไฟล์ ซึ่งเป็นไฟล์ที่จัดอยู่ในโฟลเดอร์ ซึ่งคุณสามารถใช้สำรวจไฟล์ของระบบปฏิบัติการ ตัวอย่างเช่น ใน Windows สำหรับผู้ใช้ชื่อต้อม รายการสิ่งที่ต้องทำของผู้ใช้อาจอยู่ใน C:\Users\Tom\Documents\ToDo.txt
ในตัวอย่างนี้ ToDo.txt
คือชื่อไฟล์ และ Users
, Tom
และ Documents
คือชื่อโฟลเดอร์ "C:" ใน Windows แสดงไดเรกทอรีรากของไดรฟ์
การทำงานแบบดั้งเดิมกับไฟล์บนเว็บ
วิธีแก้ไขรายการสิ่งที่ต้องทำในเว็บแอปพลิเคชันมีขั้นตอนดังนี้
- ผู้ใช้อัปโหลดไฟล์ไปยังเซิร์ฟเวอร์หรือเปิดไฟล์ในไคลเอ็นต์ด้วย
<input type="file">
- ผู้ใช้ทำการเปลี่ยนแปลง จากนั้นดาวน์โหลดไฟล์ผลลัพธ์ที่มี
<a download="ToDo.txt>
ที่แทรกซึ่งคุณใช้click()
แบบเป็นโปรแกรมผ่าน JavaScript - ในการเปิดโฟลเดอร์ ให้ใช้แอตทริบิวต์พิเศษใน
<input type="file" webkitdirectory>
ซึ่งมีการรองรับเบราว์เซอร์ที่เป็นสากลเชิงปฏิบัติ แม้ว่าจะมีชื่อที่เป็นกรรมสิทธิ์ก็ตาม
วิธีการทำงานสมัยใหม่กับไฟล์บนเว็บ
ขั้นตอนนี้ไม่ได้แสดงถึงมุมมองที่ผู้ใช้มีต่อการแก้ไขไฟล์ แต่ท้ายที่สุดแล้วผู้ใช้ก็จะได้ดาวน์โหลดสำเนาไฟล์อินพุตของตัวเอง ดังนั้น File System Access API จึงมีวิธีเครื่องมือเลือก 3 วิธี ได้แก่ showOpenFilePicker()
, showSaveFilePicker()
และ showDirectoryPicker()
ซึ่งตอบสนองตามชื่อที่แนะนำทุกประการ โฆษณาประเภทนี้จะเปิดใช้ขั้นตอนดังนี้
- เปิด
ToDo.txt
ด้วยshowOpenFilePicker()
แล้วรับออบเจ็กต์FileSystemFileHandle
- จากออบเจ็กต์
FileSystemFileHandle
ให้สร้างFile
โดยเรียกใช้เมธอดgetFile()
ของแฮนเดิลไฟล์ - แก้ไขไฟล์แล้วเรียก
requestPermission({mode: 'readwrite'})
บนแฮนเดิล - หากผู้ใช้ยอมรับคำขอสิทธิ์ ให้บันทึกการเปลี่ยนแปลงกลับไปที่ไฟล์ต้นฉบับ
- หรือโทรหา
showSaveFilePicker()
แล้วให้ผู้ใช้เลือกไฟล์ใหม่ (หากผู้ใช้เลือกไฟล์ที่เปิดก่อนหน้านี้ เนื้อหาในไฟล์จะถูกเขียนทับ) สำหรับการบันทึกซ้ำ คุณสามารถเก็บไว้ที่แฮนเดิลไฟล์ จะได้ไม่ต้องแสดงกล่องโต้ตอบการบันทึกไฟล์อีกครั้ง
ข้อจำกัดของการทำงานกับไฟล์บนเว็บ
ไฟล์และโฟลเดอร์ที่เข้าถึงได้ผ่านวิธีการเหล่านี้จะอยู่ในระบบไฟล์ที่ผู้ใช้มองเห็นได้ ไฟล์ที่บันทึกไว้จากเว็บและไฟล์ปฏิบัติการจะมีเครื่องหมายของเว็บ จึงมีคำเตือนเพิ่มเติมที่ระบบปฏิบัติการสามารถแสดงก่อนที่ไฟล์ที่อาจเป็นอันตรายจะถูกดำเนินการ เนื่องจากเป็นฟีเจอร์ความปลอดภัยเพิ่มเติม ไฟล์ที่ได้รับจากเว็บยังได้รับการปกป้องโดย Google Safe Browsing ด้วย ซึ่งคุณมองว่านี่เป็นการสแกนไวรัสในระบบคลาวด์เพื่อความสะดวกและในบริบทของบทความนี้ เมื่อเขียนข้อมูลลงในไฟล์โดยใช้ File System Access API การเขียนจะไม่มีผล แต่จะใช้ไฟล์ชั่วคราว ตัวไฟล์จะไม่มีการแก้ไขเว้นแต่จะผ่านการตรวจสอบความปลอดภัยทั้งหมดนี้ คุณคงพอจะทราบดีว่าการดำเนินการดังกล่าวทำให้การดำเนินการกับไฟล์ค่อนข้างช้า แม้จะมีการปรับปรุงการดำเนินการต่างๆ ที่ทำได้แล้ว เช่น ใน macOS ทั้งนี้ การเรียกใช้ write()
ทุกครั้งจะเป็นแบบจบในตัว ดังนั้นระบบจะเปิดไฟล์ภายใน หาออฟเซ็ตที่กำหนดไว้ และเขียนข้อมูลในท้ายที่สุด
ไฟล์ที่เป็นพื้นฐานของการประมวลผล
ในขณะเดียวกัน ไฟล์ก็เป็นวิธีที่ยอดเยี่ยมในการบันทึกข้อมูล ตัวอย่างเช่น SQLite จะจัดเก็บฐานข้อมูลทั้งหมดไว้ในไฟล์เดียว อีกตัวอย่างหนึ่งคือ mipmaps ที่ใช้ในการประมวลผลรูปภาพ Mipmaps คือลำดับของรูปภาพที่คำนวณไว้ล่วงหน้าและเพิ่มประสิทธิภาพ โดยแต่ละรูปภาพจะแสดงความละเอียดน้อยลงเรื่อยๆ จากรูปภาพก่อนหน้า ซึ่งทำให้การดำเนินการหลายอย่าง เช่น การซูมเร็วขึ้น แล้วเว็บแอปพลิเคชันจะได้รับประโยชน์จากไฟล์ได้อย่างไร แต่โดยไม่ต้องเสียค่าใช้จ่ายสำหรับประสิทธิภาพของการประมวลผลไฟล์บนเว็บ คำตอบคือระบบไฟล์ส่วนตัวต้นทาง
ระบบไฟล์ส่วนตัวที่ผู้ใช้มองเห็นได้เทียบกับระบบไฟล์ส่วนตัวที่เป็นต้นทาง
ระบบไฟล์ส่วนตัวที่ผู้ใช้มองเห็นได้ซึ่งเรียกดูโดยใช้โปรแกรมสำรวจไฟล์ของระบบปฏิบัติการ ตรงที่มีไฟล์และโฟลเดอร์ที่คุณสามารถอ่าน เขียน ย้าย และเปลี่ยนชื่อ ระบบไฟล์ส่วนตัวต้นทางไม่ได้แสดงต่อผู้ใช้ ไฟล์และโฟลเดอร์ในระบบไฟล์ส่วนตัวแบบต้นทางจะเป็นส่วนตัวและเป็นรูปธรรมมากขึ้น เป็นส่วนตัวสำหรับต้นทางของเว็บไซต์ ค้นพบต้นทางของหน้าโดยพิมพ์ 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()
โปรดดูแผนภาพต่อไปนี้เพื่อเปรียบเทียบระบบไฟล์ที่ผู้ใช้มองเห็นกับระบบไฟล์ส่วนตัวต้นทาง แผนภาพนี้แสดงให้เห็นว่านอกเหนือจากไดเรกทอรีรากแล้ว ส่วนอื่นๆ ที่เหลือยังมีแนวคิดเหมือนกัน โดยมีลำดับชั้นของไฟล์และโฟลเดอร์เพื่อจัดระเบียบและจัดเตรียมข้อมูลและพื้นที่เก็บข้อมูลตามความต้องการของคุณ
ข้อมูลจำเพาะของระบบไฟล์ส่วนตัวที่เป็นต้นทาง
เช่นเดียวกับกลไกการจัดเก็บข้อมูลอื่นๆ ในเบราว์เซอร์ (ตัวอย่างเช่น 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);
}
แก้ไขข้อบกพร่องระบบไฟล์ส่วนตัวของต้นทาง
ใช้ส่วนขยาย Chrome OPFS Explorer เพื่อแก้ไขข้อบกพร่องของระบบไฟล์ส่วนตัวต้นทางจนกว่าจะมีการเพิ่มการรองรับเครื่องมือสำหรับนักพัฒนาเว็บในตัว (ดู crbug/1284595) ภาพหน้าจอด้านบนจากส่วนการสร้างไฟล์และโฟลเดอร์ใหม่จะดึงมาจากส่วนขยายโดยตรง
หลังจากติดตั้งส่วนขยายแล้ว ให้เปิดเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome เลือกแท็บ OPFS Explorer จากนั้นคุณจะพร้อมที่จะตรวจสอบลำดับชั้นของไฟล์ บันทึกไฟล์จากระบบไฟล์ส่วนตัวต้นทางไปยังระบบไฟล์ที่ผู้ใช้มองเห็นได้โดยคลิกชื่อไฟล์ แล้วลบไฟล์และโฟลเดอร์โดยคลิกที่ไอคอนถังขยะ
สาธิต
ดูการทำงานของระบบไฟล์ส่วนตัวต้นทาง (หากคุณติดตั้งส่วนขยาย OPFS Explorer) ในการสาธิตที่ใช้เป็นแบ็กเอนด์สำหรับฐานข้อมูล SQLite ที่คอมไพล์ไปยัง WebAssembly อย่าลืมอ่านซอร์สโค้ดใน Glitch โปรดสังเกตว่าเวอร์ชันที่ฝังอยู่ด้านล่างไม่ได้ใช้แบ็กเอนด์ระบบไฟล์ส่วนตัวต้นทาง (เนื่องจาก iframe เป็นแบบข้ามต้นทาง) แต่เมื่อคุณเปิดการสาธิตในแท็บแยกต่างหาก ก็จะใช้
บทสรุป
ระบบไฟล์ส่วนตัวที่เป็นต้นทางดังที่ระบุใน WHATWG ได้เข้ามากำหนดวิธีที่เราใช้และโต้ตอบกับไฟล์บนเว็บ โดยเปิดใช้ Use Case ใหม่ๆ ซึ่งไม่สามารถทำได้ด้วยระบบไฟล์ที่ผู้ใช้มองเห็นได้ ผู้ให้บริการเบราว์เซอร์รายใหญ่ทั้งหมด ไม่ว่าจะเป็น Apple, Mozilla และ Google ก็ทำงานร่วมกันและแลกเปลี่ยนวิสัยทัศน์ร่วมกัน การพัฒนาระบบไฟล์ส่วนตัวที่เป็นต้นทางนั้นต้องอาศัยความร่วมมือกันอย่างมาก และความคิดเห็นจากนักพัฒนาซอฟต์แวร์และผู้ใช้ก็มีความสำคัญต่อความก้าวหน้า ในขณะที่เราปรับแต่งและปรับปรุงมาตรฐานอย่างต่อเนื่อง เรายินดีรับฟังความคิดเห็นเกี่ยวกับที่เก็บ whatwg/fs ในรูปแบบของปัญหาหรือคำขอพุล
ลิงก์ที่เกี่ยวข้อง
- ข้อกำหนดมาตรฐานของระบบไฟล์
- ที่เก็บแบบมาตรฐานของระบบไฟล์
- File System API กับโพสต์ WebKit ของระบบไฟล์ส่วนตัวต้นทาง
- ส่วนขยาย OPFS Explorer
กิตติกรรมประกาศ
บทความนี้ได้รับการตรวจสอบโดย Austin Sully, Etienne Noël และ Rachel Andrew ภาพหลักโดย Christina Rumpf ใน Unsplash