File System Access & OPFS
สองวิธีในการเขียนไฟล์
หัวข้อที่มีชื่อว่า “สองวิธีในการเขียนไฟล์”browser มีพื้นที่เขียนไฟล์สองแบบ:
- File System Access API (
showOpenFilePicker,showSaveFilePicker) — ให้ผู้ใช้เลือกไฟล์จริงจาก file system ในเครื่อง ต้องใช้ user gesture (คลิกปุ่ม) ไม่รองรับในทุก browser - Origin Private File System (OPFS) — virtual file system แบบ sandboxed scoped ตาม origin ไม่ต้องใช้ user gesture, ไม่มี file picker, เขียน program ได้ทั้งหมด ทำงานใน worker ใช้ quota เหมือน IndexedDB
File System Access API
หัวข้อที่มีชื่อว่า “File System Access API”ฟังก์ชัน picker จะแสดง dialog ไฟล์ของ OS ต้องเรียกจาก user-gesture handler
// Open a file chosen by the userasync function openFile() { // showOpenFilePicker must be called inside a user gesture (click, etc.) const [fileHandle] = await window.showOpenFilePicker(); const file = await fileHandle.getFile(); const text = await file.text(); console.log('File contents:', text.slice(0, 200));}
// Save / overwrite a fileasync function saveFile(content) { const handle = await window.showSaveFilePicker({ suggestedName: 'output.txt', types: [{ description: 'Text file', accept: { 'text/plain': ['.txt'] } }], }); const writable = await handle.createWritable(); await writable.write(content); await writable.close();}เนื่องจาก showOpenFilePicker และ showSaveFilePicker ต้องใช้ user gesture จึงไม่สามารถรันใน StorageRunner ข้างต้นได้โดยอัตโนมัติ ตัวอย่าง OPFS ด้านล่างรันได้เต็มรูปแบบ
Origin Private File System (OPFS)
หัวข้อที่มีชื่อว่า “Origin Private File System (OPFS)”OPFS ให้ directory tree ส่วนตัวแก่ทุก origin ไม่ต้องใช้ user gesture — แค่เรียก navigator.storage.getDirectory()
// Get the root of the OPFSconst root = await navigator.storage.getDirectory();
// Create or open a file handleconst fileHandle = await root.getFileHandle('notes.txt', { create: true });
// Write to itconst writable = await fileHandle.createWritable();await writable.write('Hello from OPFS!');await writable.close();
// Read it backconst file = await fileHandle.getFile();const text = await file.text();console.log(text); // 'Hello from OPFS!'การนำทางใน directory
หัวข้อที่มีชื่อว่า “การนำทางใน directory”const root = await navigator.storage.getDirectory();
// Create a sub-directoryconst subDir = await root.getDirectoryHandle('drafts', { create: true });
// List entriesfor await (const [name, handle] of subDir.entries()) { console.log(handle.kind, name);}รันได้: OPFS write และ read
หัวข้อที่มีชื่อว่า “รันได้: OPFS write และ read”ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
| File System Access API | เข้าถึงไฟล์จริงบน disk ที่ผู้ใช้เลือกเอง เหมาะกับ import/export ไฟล์ให้ผู้ใช้เห็นและจัดการได้ | ต้องใช้ user gesture ทุกครั้ง และไม่รองรับใน Firefox/Safari เท่า Chromium-based browser |
| OPFS | I/O เร็วใกล้เคียง native เพราะเป็น sandboxed virtual file system เขียน program ได้เต็มที่ ไม่ต้อง user gesture | browser support จำกัดกว่า IndexedDB/localStorage และผู้ใช้มองไม่เห็นไฟล์โดยตรง เพราะไม่ใช่ไฟล์ใน OS จริง |
| IndexedDB (เก็บ blob ทั่วไป) | รองรับ browser กว้างที่สุด มี transaction model ที่คุ้นเคยจากบทก่อนหน้า | throughput ต่ำกว่า OPFS สำหรับไฟล์ขนาดใหญ่หรือการเขียนบ่อยๆ |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”- คิดว่า File System Access API รองรับทุก browser เหมือน OPFS —
showOpenFilePicker/showSaveFilePickerใช้ได้เต็มที่เฉพาะ Chromium-based browser เท่านั้น Firefox และ Safari ยังไม่รองรับ - เรียก
showOpenFilePickerนอก user-gesture handler เช่นในuseEffectหรือ event ที่ไม่ใช่ click แล้วสงสัยว่าทำไม throwSecurityError - ลืมเรียก
writable.close()หลังเขียนไฟล์ใน OPFS — ข้อมูลจะไม่ถูก flush และมองไม่เห็นใน read ครั้งถัดไปแม้write()จะ resolve แล้วก็ตาม
💡 ตัวอย่างจากของจริง
VS Code for the Web — ใช้ OPFS เก็บ local cache ของไฟล์ ทำให้ file I/O เร็วใกล้เคียง native editor เมื่อรันอยู่ใน browser
Photoshop (web version) — ใช้ File System Access API ผสมกับ OPFS จัดการไฟล์ภาพขนาดใหญ่ โดยไม่ต้องโหลดทั้งไฟล์เข้า memory ทีเดียว