ข้ามไปยังเนื้อหา

File System Access & OPFS

browser มีพื้นที่เขียนไฟล์สองแบบ:

  1. File System Access API (showOpenFilePicker, showSaveFilePicker) — ให้ผู้ใช้เลือกไฟล์จริงจาก file system ในเครื่อง ต้องใช้ user gesture (คลิกปุ่ม) ไม่รองรับในทุก browser
  2. Origin Private File System (OPFS) — virtual file system แบบ sandboxed scoped ตาม origin ไม่ต้องใช้ user gesture, ไม่มี file picker, เขียน program ได้ทั้งหมด ทำงานใน worker ใช้ quota เหมือน IndexedDB

ฟังก์ชัน picker จะแสดง dialog ไฟล์ของ OS ต้องเรียกจาก user-gesture handler

// Open a file chosen by the user
async 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 file
async 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 ด้านล่างรันได้เต็มรูปแบบ

OPFS ให้ directory tree ส่วนตัวแก่ทุก origin ไม่ต้องใช้ user gesture — แค่เรียก navigator.storage.getDirectory()

// Get the root of the OPFS
const root = await navigator.storage.getDirectory();
// Create or open a file handle
const fileHandle = await root.getFileHandle('notes.txt', { create: true });
// Write to it
const writable = await fileHandle.createWritable();
await writable.write('Hello from OPFS!');
await writable.close();
// Read it back
const file = await fileHandle.getFile();
const text = await file.text();
console.log(text); // 'Hello from OPFS!'
const root = await navigator.storage.getDirectory();
// Create a sub-directory
const subDir = await root.getDirectoryHandle('drafts', { create: true });
// List entries
for await (const [name, handle] of subDir.entries()) {
console.log(handle.kind, name);
}
Browser Storage
ตัวเลือกBenefitCost
File System Access APIเข้าถึงไฟล์จริงบน disk ที่ผู้ใช้เลือกเอง เหมาะกับ import/export ไฟล์ให้ผู้ใช้เห็นและจัดการได้ต้องใช้ user gesture ทุกครั้ง และไม่รองรับใน Firefox/Safari เท่า Chromium-based browser
OPFSI/O เร็วใกล้เคียง native เพราะเป็น sandboxed virtual file system เขียน program ได้เต็มที่ ไม่ต้อง user gesturebrowser support จำกัดกว่า IndexedDB/localStorage และผู้ใช้มองไม่เห็นไฟล์โดยตรง เพราะไม่ใช่ไฟล์ใน OS จริง
IndexedDB (เก็บ blob ทั่วไป)รองรับ browser กว้างที่สุด มี transaction model ที่คุ้นเคยจากบทก่อนหน้าthroughput ต่ำกว่า OPFS สำหรับไฟล์ขนาดใหญ่หรือการเขียนบ่อยๆ
  • คิดว่า File System Access API รองรับทุก browser เหมือน OPFSshowOpenFilePicker/showSaveFilePicker ใช้ได้เต็มที่เฉพาะ Chromium-based browser เท่านั้น Firefox และ Safari ยังไม่รองรับ
  • เรียก showOpenFilePicker นอก user-gesture handler เช่นใน useEffect หรือ event ที่ไม่ใช่ click แล้วสงสัยว่าทำไม throw SecurityError
  • ลืมเรียก 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 ทีเดียว

ทำไม showOpenFilePicker ต้องใช้ user gesture?
navigator.storage.getDirectory() คืนค่าอะไร?
หลังจากเรียก fileHandle.createWritable() และเขียนข้อมูลแล้ว ต้องทำอะไรก่อนที่ข้อมูลจะมองเห็นได้ใน future reads?
ข้อใดอธิบาย OPFS storage persistence ได้ถูกต้อง?