Standar Sistem File memperkenalkan sistem file pribadi asal (OPFS) sebagai endpoint penyimpanan pribadi untuk asal halaman dan tidak terlihat oleh pengguna yang menyediakan akses opsional ke jenis file khusus yang sangat dioptimalkan untuk performa.
Dukungan browser
Sistem file pribadi origin didukung oleh browser modern dan distandardisasi oleh Web Hypertext Application Technology Working Group (WHATWG) dalam File System Living Standard.
Motivasi
Saat memikirkan file di komputer, Anda mungkin memikirkan hierarki file: file yang disusun dalam folder yang dapat Anda jelajahi dengan penjelajah file sistem operasi Anda. Misalnya, di Windows, untuk pengguna bernama Tom, daftar To
Do-nya mungkin ada di C:\Users\Tom\Documents\ToDo.txt. Dalam contoh ini,
ToDo.txt adalah nama file, dan Users, Tom, dan Documents adalah nama
folder. `C:` di Windows merepresentasikan direktori root drive.
Cara umum untuk bekerja dengan file di web
Untuk mengedit Daftar tugas di aplikasi web, berikut alur yang biasa dilakukan:
- Pengguna mengupload file ke server atau membukanya di klien dengan
<input type="file">. - Pengguna membuat perubahan, lalu mendownload file yang dihasilkan dengan
<a download="ToDo.txt>yang disisipkan yang Andaclick()secara terprogram dengan JavaScript. - Untuk membuka folder, Anda menggunakan atribut khusus di
<input type="file" webkitdirectory>, yang, meskipun memiliki nama eksklusif, memiliki dukungan browser yang hampir universal.
Cara modern untuk bekerja dengan file di web
Alur ini tidak menggambarkan cara pengguna berpikir tentang pengeditan file, dan berarti pengguna akan mendapatkan salinan file input yang didownload. Oleh karena itu, File System Access API memperkenalkan tiga metode pemilih:
Fitur ini memungkinkan alur sebagai berikut:
- Buka
ToDo.txtdenganshowOpenFilePicker(), dan dapatkan objekFileSystemFileHandle. - Dari objek
FileSystemFileHandle, dapatkanFiledengan memanggil metodegetFile()handle file. - Ubah file, lalu panggil
requestPermission({mode: 'readwrite'})pada handle. - Jika pengguna menyetujui permintaan izin, simpan kembali perubahan ke file asli.
- Atau, panggil
showSaveFilePicker()dan biarkan pengguna memilih file baru. (Jika pengguna memilih file yang sebelumnya dibuka, isinya akan ditimpa.) Untuk penyimpanan berulang, Anda dapat menyimpan handle file, sehingga Anda tidak perlu menampilkan dialog penyimpanan file lagi.
Batasan saat bekerja dengan file di web
File dan folder yang dapat diakses dengan metode ini berada di sistem file yang dapat disebut sebagai terlihat oleh pengguna. File yang disimpan dari web, dan file yang dapat dieksekusi secara khusus, ditandai dengan tanda web, sehingga ada peringatan tambahan yang dapat ditampilkan sistem operasi sebelum file yang berpotensi berbahaya dieksekusi. Sebagai fitur keamanan tambahan, file yang diperoleh dari web juga dilindungi oleh Safe Browsing, yang, demi kesederhanaan dan dalam konteks dokumen ini, dapat Anda anggap sebagai pemindaian virus berbasis cloud. Saat Anda menulis data ke file menggunakan File System Access API, penulisan tidak dilakukan di tempat, tetapi menggunakan file sementara. File itu sendiri tidak dimodifikasi kecuali jika lulus semua pemeriksaan keamanan ini.
Tugas ini membuat operasi file relatif lambat, meskipun ada peningkatan yang diterapkan jika memungkinkan, misalnya, di macOS.
Namun, setiap panggilan write() bersifat mandiri, sehingga di balik layar, panggilan ini membuka file, mencari offset yang diberikan, dan akhirnya menulis data.
File sebagai dasar pemrosesan
Pada saat yang sama, file adalah cara yang sangat baik untuk merekam data. Misalnya, SQLite menyimpan seluruh database dalam satu file. Contoh lainnya adalah mipmap yang digunakan dalam pemrosesan gambar. Mipmap adalah urutan gambar yang telah dihitung sebelumnya dan dioptimalkan, yang masing-masing merupakan representasi resolusi yang semakin rendah dari gambar sebelumnya, sehingga mempercepat banyak operasi seperti melakukan zoom. Jadi, bagaimana cara aplikasi web mendapatkan manfaat file, tetapi tanpa biaya performa pemrosesan file berbasis web? Jawabannya adalah sistem file pribadi origin.
Sistem file pribadi origin versus yang terlihat oleh pengguna
Tidak seperti sistem file yang terlihat oleh pengguna yang dijelajahi menggunakan file explorer sistem operasi, dengan file dan folder yang dapat Anda baca, tulis, pindahkan, dan ganti namanya, sistem file pribadi asal tidak dimaksudkan untuk dilihat oleh pengguna. File dan folder
di sistem file pribadi origin, seperti namanya, bersifat pribadi, dan lebih
konkretnya, bersifat pribadi untuk origin
situs. Temukan asal halaman dengan mengetik
location.origin
di Konsol DevTools. Misalnya, asal halaman
https://developer.chrome.com/articles/ adalah https://developer.chrome.com.
Baca selengkapnya tentang teori asal-usul di
Memahami "situs yang sama" dan "origin yang sama".
Semua halaman yang memiliki origin yang sama dapat melihat data sistem file pribadi origin yang sama, sehingga https://developer.chrome.com/docs/extensions/mv3/getstarted/extensions-101/
dapat melihat detail yang sama seperti contoh sebelumnya. Setiap origin memiliki sistem file pribadi origin independennya sendiri, yang berarti sistem file pribadi origin https://developer.chrome.com benar-benar berbeda dari sistem file pribadi origin, misalnya, https://web.dev. Di Windows, direktori root sistem file yang terlihat oleh pengguna adalah C:\\.
Yang setara untuk sistem file pribadi origin adalah direktori root yang awalnya kosong per origin yang diakses dengan memanggil metode asinkron
navigator.storage.getDirectory().
Untuk perbandingan sistem file yang terlihat oleh pengguna dan sistem file pribadi origin, lihat diagram berikut. Diagram menunjukkan bahwa selain direktori root, semuanya secara konseptual sama, dengan hierarki file dan folder untuk mengatur dan menyusun sesuai kebutuhan data dan penyimpanan Anda.
navigator.storage.getDirectory.Spesifikasi sistem file pribadi origin
Sama seperti mekanisme penyimpanan lainnya di browser (misalnya, localStorage atau IndexedDB), sistem file pribadi asal tunduk pada batasan kuota browser. Jika pengguna menghapus semua data penjelajahan atau semua data situs, sistem file pribadi asal juga akan dihapus.
Panggil navigator.storage.estimate()
dan di objek respons yang dihasilkan, lihat entri
usage
untuk melihat seberapa banyak penyimpanan yang sudah digunakan aplikasi Anda, yang dikelompokkan berdasarkan
mekanisme penyimpanan dalam objek usageDetails,
tempat Anda ingin melihat entri fileSystem secara khusus. Karena sistem file pribadi asal tidak terlihat oleh pengguna, tidak ada perintah izin dan tidak ada pemeriksaan Safe Browsing.
Mendapatkan akses ke direktori root
Untuk mendapatkan akses ke direktori root, jalankan perintah berikut. Anda akan mendapatkan
handle direktori kosong, lebih tepatnya, sebuah
FileSystemDirectoryHandle.
const opfsRoot = await navigator.storage.getDirectory();
// A FileSystemDirectoryHandle whose type is "directory"
// and whose name is "".
console.log(opfsRoot);
Thread utama atau Web Worker
Ada dua cara untuk menggunakan sistem file pribadi origin: di thread utama atau di Web Worker. Web Worker tidak dapat memblokir thread utama, yang berarti dalam konteks ini, API dapat bersifat sinkron, sebuah pola yang umumnya tidak diizinkan di thread utama. API sinkron dapat lebih cepat karena tidak perlu menangani promise, dan operasi file biasanya sinkron dalam bahasa seperti C yang dapat dikompilasi ke WebAssembly.
// This is synchronous C code.
FILE *f;
f = fopen("example.txt", "w+");
fputs("Some text\n", f);
fclose(f);
Jika Anda memerlukan operasi file secepat mungkin, atau Anda berurusan dengan WebAssembly, lanjutkan ke bagian Menggunakan sistem file pribadi asal di Web Worker.
Menggunakan sistem file pribadi asal di thread utama
Membuat file dan folder baru
Setelah memiliki folder root, buat file dan folder menggunakan
metode getFileHandle()
dan getDirectoryHandle(). Dengan meneruskan
{create: true},
file atau folder akan dibuat jika belum ada. Buat hierarki
file dengan memanggil fungsi ini menggunakan direktori yang baru dibuat sebagai
titik awal.
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});

Mengakses file dan folder yang ada
Jika Anda mengetahui namanya, akses file dan folder yang dibuat sebelumnya dengan memanggil
metode getFileHandle() atau getDirectoryHandle(), dengan meneruskan
nama file atau folder.
const existingFileHandle = await opfsRoot.getFileHandle('my first file');
const existingDirectoryHandle = await opfsRoot
.getDirectoryHandle('my first folder');
Mendapatkan file yang terkait dengan handle file untuk dibaca
FileSystemFileHandle mewakili file di sistem file. Untuk mendapatkan
File terkait, gunakan metode getFile(). Objek File adalah jenis
Blob tertentu, dan dapat digunakan dalam
konteks apa pun yang dapat dilakukan oleh Blob.
Secara khusus, FileReader,
URL.createObjectURL(),
createImageBitmap(),
dan XMLHttpRequest.send()
menerima Blobs dan Files. Mendapatkan File dari
FileSystemFileHandle "membebaskan" data, sehingga Anda dapat mengaksesnya dan menyediakannya
untuk sistem file yang terlihat oleh pengguna.
const file = await fileHandle.getFile();
console.log(await file.text());
Menulis ke file dengan streaming
Streaming data ke dalam file dengan memanggil
createWritable()
yang membuat FileSystemWritableFileStream
yang kemudian Anda write()
kontennya. Pada akhirnya, Anda perlu
close()
aliran.
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();
Menghapus file dan folder
Hapus file dan folder dengan memanggil metode
remove()
tertentu dari handle file atau direktori. Untuk menghapus folder termasuk semua subfolder, teruskan opsi
{recursive: true}.
await fileHandle.remove();
await directoryHandle.remove({recursive: true});
Sebagai alternatif, jika Anda mengetahui nama file atau folder yang akan dihapus dalam
direktori, gunakan metode removeEntry().
directoryHandle.removeEntry('my first nested file');
Memindahkan dan mengganti nama file dan folder
Ganti nama dan pindahkan file dan folder menggunakan metode
move(). Pemindahan dan penggantian nama dapat terjadi bersamaan atau terpisah.
// 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');
Menyelesaikan jalur file atau folder
Untuk mempelajari lokasi file atau folder tertentu dalam kaitannya dengan direktori
referensi, gunakan metode resolve()
dengan meneruskan FileSystemHandle sebagai argumen. Untuk mendapatkan jalur lengkap
file atau folder dalam sistem file pribadi asal, gunakan direktori root sebagai
direktori referensi yang diperoleh menggunakan navigator.storage.getDirectory().
const relativePath = await opfsRoot.resolve(nestedDirectoryHandle);
// `relativePath` is `['my first folder', 'my first nested folder']`.
Memeriksa apakah dua handle file atau folder mengarah ke file atau folder yang sama
Terkadang Anda memiliki dua handle dan tidak tahu apakah keduanya mengarah ke file atau folder yang sama. Untuk memeriksa apakah hal ini terjadi, gunakan metode
isSameEntry().
fileHandle.isSameEntry(nestedFileHandle);
// Returns `false`.
Mencantumkan konten folder
FileSystemDirectoryHandle adalah
iterator asinkron
yang Anda iterasi dengan
loop
for await... of. Sebagai iterator asinkron, iterator ini juga mendukung metode
entries(),
values(),
dan keys(), yang dapat Anda pilih bergantung pada informasi yang Anda butuhkan:
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()) {}
Mencantumkan konten folder dan semua subfolder secara rekursif
Menangani loop dan fungsi asinkron yang dipasangkan dengan rekursi mudah
salah. Fungsi berikut dapat berfungsi sebagai titik awal untuk mencantumkan
konten folder dan semua subfoldernya, termasuk semua file dan ukurannya. Anda dapat menyederhanakan fungsi jika tidak memerlukan ukuran file dengan, di mana
tertulis directoryEntryPromises.push, tidak mendorong promise handle.getFile(), tetapi handle secara langsung.
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;
};
Menggunakan sistem file pribadi asal di Web Worker
Seperti yang diuraikan sebelumnya, Web Worker tidak dapat memblokir thread utama, itulah sebabnya dalam konteks ini metode sinkron diizinkan.
Mendapatkan handle akses sinkron
Titik entri ke operasi file secepat mungkin adalah
FileSystemSyncAccessHandle,
yang diperoleh dari
FileSystemFileHandle
biasa dengan memanggil createSyncAccessHandle().
const fileHandle = await opfsRoot
.getFileHandle('my highspeed file.txt', {create: true});
const syncAccessHandle = await fileHandle.createSyncAccessHandle();
Metode file di tempat sinkron
Setelah memiliki handle akses sinkron, Anda akan mendapatkan akses ke metode file di tempat yang cepat yang semuanya sinkron.
getSize(): Menampilkan ukuran file dalam byte.write(): Menulis konten buffer ke dalam file, secara opsional pada offset tertentu, dan menampilkan jumlah byte yang ditulis. Memeriksa jumlah byte yang ditulis yang ditampilkan memungkinkan pemanggil mendeteksi dan menangani error serta penulisan sebagian.read(): Membaca konten file ke dalam buffer, secara opsional pada offset tertentu.truncate(): Mengubah ukuran file ke ukuran yang ditentukan.flush(): Memastikan bahwa konten file berisi semua modifikasi yang dilakukan melaluiwrite().close(): Menutup handle akses.
Berikut adalah contoh yang menggunakan setiap metode.
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);
Menyalin file dari sistem file pribadi asal ke sistem file yang dapat dilihat pengguna
Seperti yang disebutkan di atas, memindahkan file dari sistem file pribadi asal ke sistem file yang terlihat pengguna tidak dapat dilakukan, tetapi Anda dapat menyalin file. Karena
showSaveFilePicker() hanya diekspos di thread utama, tetapi tidak di thread
Worker, pastikan untuk menjalankan kode di sana.
// 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);
}
Men-debug sistem file pribadi origin
Hingga dukungan DevTools bawaan ditambahkan (lihat crbug/1284595), gunakan ekstensi Chrome OPFS Explorer untuk men-debug sistem file pribadi origin. Screenshot dari bagian Membuat file dan folder baru diambil langsung dari ekstensi.

Setelah menginstal ekstensi, buka Chrome DevTools, pilih tab OPFS Explorer, dan Anda siap memeriksa hierarki file. Simpan file dari sistem file pribadi asal ke sistem file yang dapat dilihat pengguna dengan mengklik nama file dan hapus file serta folder dengan mengklik ikon tempat sampah.
Demo
Lihat cara kerja sistem file pribadi origin (jika Anda menginstal ekstensi OPFS Explorer) dalam demo yang menggunakannya sebagai backend untuk database SQLite yang dikompilasi ke WebAssembly. Pastikan untuk melihat kode sumber di GitHub. Perhatikan bagaimana versi tersemat tidak menggunakan backend sistem file pribadi origin (karena iframe adalah lintas origin), tetapi saat Anda membuka demo di tab terpisah, versi tersemat akan menggunakannya.
Kesimpulan
Sistem file pribadi origin, sebagaimana ditentukan oleh WHATWG, telah membentuk cara kita menggunakan dan berinteraksi dengan file di web. Hal ini telah memungkinkan kasus penggunaan baru yang tidak mungkin dicapai dengan sistem file yang terlihat oleh pengguna. Semua vendor browser utama—Apple, Mozilla, dan Google—telah bergabung dan memiliki visi yang sama. Pengembangan sistem file pribadi origin merupakan upaya kolaboratif, dan masukan dari developer serta pengguna sangat penting untuk kemajuannya.
Seiring kami terus menyempurnakan dan meningkatkan kualitas standar, kami menerima masukan terkait repositori whatwg/fs dalam bentuk Masalah atau Permintaan Pull.
Link terkait
- Spesifikasi Standar Sistem File
- Repositori Standar Sistem File
- File System API dengan postingan WebKit Origin Private File System
- Ekstensi OPFS Explorer
Ucapan terima kasih
Dokumen ini ditinjau oleh Austin Sully, Etienne Noël, dan Rachel Andrew.