Browser telah lama dapat menangani file dan direktori. File API menyediakan fitur untuk merepresentasikan objek file di aplikasi web, serta memilihnya secara terprogram dan mengakses datanya. Namun, saat Anda melihat lebih dekat, semua yang berkilau bukanlah emas.
Cara tradisional dalam menangani file
Membuka file
Sebagai developer, Anda dapat membuka dan membaca file melalui elemen
<input type="file">
.
Dalam bentuknya yang paling sederhana, membuka file dapat terlihat seperti contoh kode di bawah.
Objek input
memberi Anda FileList
,
yang dalam kasus di bawah hanya terdiri dari satu
File
.
File
adalah jenis Blob
tertentu,
dan dapat digunakan dalam konteks apa pun yang dapat dilakukan oleh Blob.
const openFile = async () => {
return new Promise((resolve) => {
const input = document.createElement('input');
input.type = 'file';
input.addEventListener('change', () => {
resolve(input.files[0]);
});
input.click();
});
};
Membuka direktori
Untuk membuka folder (atau direktori), Anda dapat menetapkan atribut
<input webkitdirectory>
.
Selain itu, semuanya berfungsi sama seperti di atas.
Meskipun memiliki nama berawalan vendor,
webkitdirectory
tidak hanya dapat digunakan di browser Chromium dan WebKit, tetapi juga di Edge berbasis EdgeHTML lama serta di Firefox.
Menyimpan (lebih tepatnya: mendownload) file
Untuk menyimpan file, biasanya Anda hanya dapat mendownload file,
yang berfungsi berkat atribut
<a download>
.
Dengan Blob, Anda dapat menyetel atribut href
penanda ke URL blob:
yang dapat Anda peroleh dari metode
URL.createObjectURL()
.
const saveFile = async (blob) => {
const a = document.createElement('a');
a.download = 'my-file.txt';
a.href = URL.createObjectURL(blob);
a.addEventListener('click', (e) => {
setTimeout(() => URL.revokeObjectURL(a.href), 30 * 1000);
});
a.click();
};
Permasalahan
Kelemahan besar dari pendekatan download adalah tidak ada cara untuk membuat alur buka→edit→simpan klasik terjadi, yaitu tidak ada cara untuk menimpa file asli. Sebagai gantinya, Anda akan mendapatkan salinan baru dari file asli di folder Download default sistem operasi setiap kali Anda "menyimpan".
File System Access API
File System Access API membuat kedua operasi, membuka dan menyimpan, menjadi jauh lebih sederhana. Fitur ini juga memungkinkan penyimpanan sebenarnya, yaitu Anda tidak hanya dapat memilih tempat untuk menyimpan file, tetapi juga menimpa file yang ada.
Membuka file
Dengan File System Access API, membuka file hanya memerlukan satu panggilan ke metode window.showOpenFilePicker()
.
Panggilan ini menampilkan handle file, yang dapat Anda gunakan untuk mendapatkan File
sebenarnya melalui metode getFile()
.
const openFile = async () => {
try {
// Always returns an array.
const [handle] = await window.showOpenFilePicker();
return handle.getFile();
} catch (err) {
console.error(err.name, err.message);
}
};
Membuka direktori
Buka direktori dengan memanggil
window.showDirectoryPicker()
yang membuat direktori dapat dipilih di kotak dialog file.
Menyimpan file
Menyimpan file juga sama sederhananya.
Dari handle file, Anda membuat stream yang dapat ditulis melalui createWritable()
,
lalu Anda menulis data Blob dengan memanggil metode write()
stream,
dan terakhir Anda menutup stream dengan memanggil metode close()
-nya.
const saveFile = async (blob) => {
try {
const handle = await window.showSaveFilePicker({
types: [{
accept: {
// Omitted
},
}],
});
const writable = await handle.createWritable();
await writable.write(blob);
await writable.close();
return handle;
} catch (err) {
console.error(err.name, err.message);
}
};
Memperkenalkan browser-fs-access
Meskipun File System Access API sangat bagus, API ini belum tersedia secara luas.

Itulah sebabnya saya melihat File System Access API sebagai progressive enhancement. Oleh karena itu, saya ingin menggunakannya saat browser mendukungnya, dan menggunakan pendekatan tradisional jika tidak; semuanya tanpa menghukum pengguna dengan download kode JavaScript yang tidak didukung yang tidak perlu. Library browser-fs-access adalah jawaban saya untuk tantangan ini.
Filosofi desain
Karena File System Access API masih mungkin berubah di masa mendatang, browser-fs-access API tidak dimodelkan setelahnya.
Artinya, library ini bukan polyfill,
melainkan ponyfill.
Anda dapat (secara statis atau dinamis) mengimpor secara eksklusif fungsi apa pun yang Anda butuhkan untuk menjaga ukuran aplikasi sekecil mungkin.
Metode yang tersedia adalah
fileOpen()
,
directoryOpen()
, dan
fileSave()
.
Secara internal, library mendeteksi fitur jika File System Access API didukung, lalu mengimpor jalur kode yang sesuai.
Menggunakan library browser-fs-access
Ketiga metode ini intuitif untuk digunakan.
Anda dapat menentukan mimeTypes
atau extensions
file yang diterima aplikasi, dan menetapkan tanda multiple
untuk mengizinkan atau tidak mengizinkan pemilihan beberapa file atau direktori.
Untuk mengetahui detail selengkapnya, lihat
dokumentasi browser-fs-access API.
Contoh kode di bawah menunjukkan cara membuka dan menyimpan file gambar.
// The imported methods will use the File
// System Access API or a fallback implementation.
import {
fileOpen,
directoryOpen,
fileSave,
} from 'https://unpkg.com/browser-fs-access';
(async () => {
// Open an image file.
const blob = await fileOpen({
mimeTypes: ['image/*'],
});
// Open multiple image files.
const blobs = await fileOpen({
mimeTypes: ['image/*'],
multiple: true,
});
// Open all files in a directory,
// recursively including subdirectories.
const blobsInDirectory = await directoryOpen({
recursive: true
});
// Save a file.
await fileSave(blob, {
fileName: 'Untitled.png',
});
})();
Demo
Anda dapat melihat cara kerja kode di atas dalam demo di GitHub. Kode sumber-nya juga tersedia di sana. Karena alasan keamanan, sub-frame lintas origin tidak diizinkan untuk menampilkan pemilih file, sehingga demo tidak dapat disematkan dalam artikel ini.
Library browser-fs-access di alam bebas
Di waktu luang, saya berkontribusi sedikit pada PWA yang dapat diinstal bernama Excalidraw, alat papan tulis yang memungkinkan Anda membuat sketsa diagram dengan mudah dengan nuansa gambar tangan. Tata letak ini sepenuhnya responsif dan berfungsi dengan baik di berbagai perangkat, mulai dari ponsel kecil hingga komputer dengan layar besar. Artinya, aplikasi perlu menangani file di semua platform, baik yang mendukung File System Access API maupun tidak. Hal ini menjadikannya kandidat yang tepat untuk library browser-fs-access.
Misalnya, saya dapat memulai gambar di iPhone, menyimpannya (secara teknis: mendownloadnya, karena Safari tidak mendukung File System Access API) ke folder Download iPhone saya, membuka file di desktop (setelah mentransfernya dari ponsel), memodifikasi file, dan menimpanya dengan perubahan saya, atau bahkan menyimpannya sebagai file baru.




Contoh kode dalam kehidupan nyata
Di bawah ini, Anda dapat melihat contoh sebenarnya browser-fs-access saat digunakan di Excalidraw.
Kutipan ini diambil dari
/src/data/json.ts
.
Yang menarik adalah bagaimana metode saveAsJSON()
meneruskan handle file atau null
ke metode fileSave()
browser-fs-access, yang menyebabkannya ditimpa saat handle diberikan, atau disimpan ke file baru jika tidak.
export const saveAsJSON = async (
elements: readonly ExcalidrawElement[],
appState: AppState,
fileHandle: any,
) => {
const serialized = serializeAsJSON(elements, appState);
const blob = new Blob([serialized], {
type: "application/json",
});
const name = `${appState.name}.excalidraw`;
(window as any).handle = await fileSave(
blob,
{
fileName: name,
description: "Excalidraw file",
extensions: ["excalidraw"],
},
fileHandle || null,
);
};
export const loadFromJSON = async () => {
const blob = await fileOpen({
description: "Excalidraw files",
extensions: ["json", "excalidraw"],
mimeTypes: ["application/json"],
});
return loadFromBlob(blob);
};
Pertimbangan UI
Baik di Excalidraw maupun aplikasi Anda,
UI harus beradaptasi dengan situasi dukungan browser.
Jika File System Access API didukung (if ('showOpenFilePicker' in window) {}
),
Anda dapat menampilkan tombol Simpan Sebagai selain tombol Simpan.
Screenshot di bawah menunjukkan perbedaan antara toolbar aplikasi utama responsif Excalidraw di iPhone dan di desktop Chrome.
Perhatikan bahwa tombol Simpan Sebagai tidak ada di iPhone.


Kesimpulan
Bekerja dengan file sistem secara teknis berfungsi di semua browser modern. Di browser yang mendukung File System Access API, Anda dapat meningkatkan kualitas pengalaman dengan mengizinkan penyimpanan dan penimpaan file yang sebenarnya (bukan hanya mendownload) dan dengan mengizinkan pengguna membuat file baru di mana pun mereka inginkan, sekaligus tetap berfungsi di browser yang tidak mendukung File System Access API. browser-fs-access mempermudah hidup Anda dengan menangani kehalusan peningkatan progresif dan membuat kode Anda sesederhana mungkin.
Ucapan terima kasih
Artikel ini ditinjau oleh Joe Medley dan Kayce Basques. Terima kasih kepada kontributor Excalidraw atas pekerjaan mereka dalam proyek ini dan atas peninjauan Permintaan Tarikan saya. Gambar banner besar oleh Ilya Pavlov di Unsplash.