Browser telah mampu menangani file dan direktori untuk waktu yang lama. File API menyediakan fitur untuk merepresentasikan objek file dalam aplikasi web, serta memilih dan mengakses datanya secara terprogram. Namun, saat Anda melihat lebih dekat, semua yang berkilau itu belum tentu emas.
Cara tradisional menangani file
Membuka file
Sebagai pengembang, Anda dapat membuka dan membaca file melalui
<input type="file">
.
Dalam bentuk yang paling sederhana, membuka file bisa terlihat seperti contoh kode di bawah ini.
Objek input
memberi Anda FileList
,
yang dalam kasus di bawah ini hanya terdiri dari satu
File
.
File
adalah jenis Blob
spesifik,
dan dapat digunakan dalam konteks apa pun
seperti yang bisa 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 mengatur atribut
<input webkitdirectory>
.
Selain itu, yang lainnya berfungsi sama seperti di atas.
Meskipun namanya
awalan vendor,
webkitdirectory
tidak hanya dapat digunakan di browser Chromium dan WebKit, tetapi juga di Edge berbasis EdgeHTML lama serta di Firefox.
Menyimpan (bukan: mendownload) file
Untuk menyimpan file, biasanya, Anda dibatasi untuk mendownload file,
yang berfungsi berkat
<a download>
.
Dengan adanya Blob, Anda dapat menetapkan atribut href
anchor ke URL blob:
yang bisa Anda dapatkan dari
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 open→edit→simpan alur terjadi, yaitu, tidak ada cara untuk menimpa file asli. Sebagai gantinya, Anda akan mendapatkan salinan baru dari file asli di folder {i>Downloads<i} {i>default <i}pada sistem operasi kapan pun Anda "menyimpan".
File System Access API
File System Access API membuat operasi, membuka dan menyimpan, menjadi jauh lebih sederhana. Alat ini juga memungkinkan penyimpanan benar, sehingga Anda tidak hanya dapat memilih tempat 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 memungkinkan Anda 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
Cara menyimpan file juga sama mudahnya.
Dari handle file, Anda membuat streaming yang dapat ditulis melalui createWritable()
,
lalu tulis data Blob dengan memanggil metode write()
aliran data,
dan terakhir, Anda menutup aliran data dengan memanggil metode close()
.
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
Sebaik apa pun File System Access API, teknologi ini belum tersedia secara luas.
![Tabel dukungan browser untuk File System Access API. Semua browser ditandai sebagai 'tidak didukung' atau 'di balik bendera'.](https://developer.chrome.google.cn/static/docs/capabilities/browser-fs-access/image/browser-support-table-th-671d3874c04e9.png?authuser=1&hl=id)
Inilah alasan saya melihat File System Access API sebagai progressive enhancement. Karena itu, saya ingin menggunakannya ketika {i>browser<i} mendukungnya, dan menggunakan pendekatan tradisional jika tidak; semua tanpa menghukum pengguna dengan mengunduh kode JavaScript yang tidak perlu dan tidak didukung. Class browser-fs-access perpustakaan adalah jawaban saya untuk tantangan ini.
Filosofi desain
Karena {i>File System Access API <i}
masih mungkin berubah di masa mendatang,
API browser-fs-access tidak dimodelkan.
Artinya, library bukan polyfill,
melainkan ponyfill.
Anda dapat (secara statis atau dinamis) mengimpor fungsi apa pun yang diperlukan untuk menjaga aplikasi Anda sekecil mungkin.
Metode yang tersedia adalah
fileOpen()
,
directoryOpen()
, dan
fileSave()
.
Secara internal, fitur library mendeteksi
apakah File System Access API didukung,
lalu mengimpor jalur kode yang sesuai.
Menggunakan library browser-fs-access
Ketiga metode tersebut intuitif untuk digunakan.
Anda dapat menentukan mimeTypes
aplikasi yang diterima atau file extensions
, dan menyetel tanda multiple
untuk mengizinkan atau melarang
pemilihan beberapa file atau direktori.
Untuk detail selengkapnya, lihat
dokumentasi API browser-fs-access.
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 Glitch. Kode sumbernya juga tersedia di sana. Karena alasan keamanan, sub frame lintas origin tidak diizinkan untuk menampilkan pemilih file, demo tidak dapat disematkan dalam artikel ini.
Library browser-fs-access secara umum
Di waktu luang saya, saya sedikit berkontribusi untuk PWA yang dapat diinstal yang disebut Excalidraw, {i>tool<i} papan tulis yang memungkinkan Anda membuat sketsa diagram dengan mudah dan terasa seperti gambar tangan. Aplikasi ini sepenuhnya responsif dan berfungsi dengan baik di berbagai perangkat, mulai dari ponsel kecil hingga komputer dengan layar besar. Artinya, sistem tersebut perlu menangani file di berbagai platform apakah mereka mendukung API Akses Sistem File atau tidak. Hal ini menjadikannya kandidat yang tepat untuk library browser-fs-access.
Saya dapat, misalnya, mulai menggambar di iPhone, simpan (secara teknis: download, karena Safari tidak mendukung File System Access API) ke folder Download di iPhone, buka file di desktop (setelah mentransfernya dari ponsel), mengubah file, dan menimpanya dengan perubahan saya, atau bahkan menyimpannya sebagai file baru.
![Gambar Excalidraw di iPhone.](https://developer.chrome.google.cn/static/docs/capabilities/browser-fs-access/image/an-excalidraw-drawing-an-b6a12825381fc.png?authuser=1&hl=id)
![Gambar Excalidraw yang dimodifikasi di Chrome pada desktop.](https://developer.chrome.google.cn/static/docs/capabilities/browser-fs-access/image/the-modified-excalidraw-d-43ed0ce25523d.png?authuser=1&hl=id)
![Menimpa file asli dengan modifikasi.](https://developer.chrome.google.cn/static/docs/capabilities/browser-fs-access/image/overwriting-original-fil-244def5df4bb.png?authuser=1&hl=id)
![Menyimpan modifikasi ke file gambar Excalidraw baru.](https://developer.chrome.google.cn/static/docs/capabilities/browser-fs-access/image/saving-modifications-a-16addfc23ebbb.png?authuser=1&hl=id)
Contoh kode di kehidupan nyata
Di bawah ini, Anda dapat melihat contoh aktual dari browser-fs-access saat digunakan di Excalidraw.
Cuplikan ini diambil dari
/src/data/json.ts
Yang perlu diperhatikan adalah cara metode saveAsJSON()
meneruskan nama sebutan file atau null
ke browser-fs-access'
fileSave()
, yang menyebabkannya menimpa saat handle diberikan,
atau untuk menyimpan
ke file baru jika belum.
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 atau aplikasi Anda,
UI harus beradaptasi dengan
situasi dukungan browser.
Jika File System Access API didukung (if ('showOpenFilePicker' in window) {}
)
Anda dapat menampilkan tombol Save As selain tombol Save.
Screenshot di bawah menunjukkan perbedaan antara toolbar aplikasi utama responsif Excalidraw di iPhone dan Chrome di desktop.
Perhatikan bagaimana tombol Save As tidak ada di iPhone.
![Toolbar aplikasi Excalidraw di iPhone hanya dengan 'Save' tombol.](https://developer.chrome.google.cn/static/docs/capabilities/browser-fs-access/image/excalidraw-app-toolbar-i-f6424de71f2f6.png?authuser=1&hl=id)
![Toolbar aplikasi Excalidraw di desktop Chrome dengan tombol 'Save' dan tab "Simpan Sebagai" tombol.](https://developer.chrome.google.cn/static/docs/capabilities/browser-fs-access/image/excalidraw-app-toolbar-c-ce0b01551edeb.png?authuser=1&hl=id)
Kesimpulan
Secara teknis, bekerja dengan file sistem berfungsi pada semua browser modern. Pada browser yang mendukung File System Access API, Anda bisa membuat pengalaman lebih baik dengan mengizinkan untuk menyimpan dan menimpa file yang sebenarnya (tidak hanya mengunduh) file dan dengan memungkinkan pengguna membuat file baru di mana pun mereka inginkan, semuanya namun tetap berfungsi pada browser yang tidak mendukung File System Access API. browser-fs-access memudahkan hidup Anda dengan menangani seluk-beluk {i>progressive enhancement<i} dan membuat kode Anda sesederhana mungkin.
Ucapan terima kasih
Artikel ini ditinjau oleh Joe Medley dan Kayce Basques. Terima kasih kepada kontributor Excalidraw untuk pekerjaan mereka pada project dan untuk meninjau Permintaan Pull saya. Banner besar oleh Ilya Pavlov di Unsplash.