Browser telah dapat menangani file dan direktori sejak 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, ternyata tidak semua yang berkilau itu emas.
Cara tradisional untuk menangani file
Membuka file
Sebagai developer, Anda dapat membuka dan membaca file melalui elemen
<input type="file">
.
Dalam bentuk yang paling sederhana, membuka file dapat terlihat seperti contoh kode di bawah.
Objek input
memberi Anda FileList
,
yang dalam kasus di bawah ini hanya terdiri dari satu
File
.
File
adalah jenis Blob
tertentu,
dan dapat digunakan dalam konteks apa pun yang dapat dilakukan 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, yang lainnya berfungsi sama seperti di atas.
Meskipun namanya diawali dengan awalan vendor,
webkitdirectory
tidak hanya dapat digunakan di browser Chromium dan WebKit, tetapi juga di Edge lama berbasis EdgeHTML serta di Firefox.
Menyimpan (atau: mendownload) file
Untuk menyimpan file, biasanya, Anda hanya perlu mendownload file,
yang berfungsi berkat
atribut
<a download>
.
Dengan Blob, Anda dapat menetapkan atribut href
anchor ke URL blob:
yang dapat Anda dapatkan 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 klasik buka→edit→simpan terjadi, yaitu, tidak ada cara untuk menulis ulang 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 operasi, membuka dan menyimpan, menjadi jauh lebih sederhana. Alat ini juga memungkinkan penyimpanan benar, sehingga Anda tidak hanya dapat memilih tempat untuk menyimpan file, tetapi juga menimpa file yang sudah ada.
Membuka file
Dengan File System Access API,
membuka file cukup dengan 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
Menyimpan file juga sama mudahnya.
Dari handle file, Anda membuat aliran yang dapat ditulis melalui createWritable()
,
lalu menulis data Blob dengan memanggil metode write()
aliran,
dan terakhir Anda menutup aliran 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 sudah sangat baik, 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 dilakukan dengan tidak menghukum pengguna dengan mendownload kode JavaScript yang tidak diperlukan. 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,
tetapi ponyfill.
Anda dapat (secara statis atau dinamis) mengimpor secara eksklusif fungsi apa pun yang diperlukan agar aplikasi Anda tetap sekecil mungkin.
Metode yang tersedia adalah metode yang diberi nama tepat
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
atau file extensions
yang diterima aplikasi, dan menetapkan tanda multiple
untuk mengizinkan atau melarang 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 Glitch. Kode sumbernya juga tersedia di sana. Karena alasan keamanan, subframe lintas origin tidak diizinkan untuk menampilkan pemilih file, demo tidak dapat disematkan dalam artikel ini.
Library browser-fs-access di dunia nyata
Di waktu luang, saya memberikan sedikit kontribusi pada PWA yang dapat diinstal yang disebut Excalidraw, alat papan tulis virtual yang memungkinkan Anda membuat sketsa diagram dengan mudah dengan sentuhan tangan. Aplikasi ini sepenuhnya responsif dan berfungsi dengan baik di berbagai perangkat, mulai dari ponsel kecil hingga komputer dengan layar besar. Artinya, aplikasi harus menangani file di berbagai platform baik yang mendukung File System Access API maupun yang tidak. Hal ini menjadikannya kandidat yang bagus untuk library browser-fs-access.
Saya dapat, misalnya, memulai menggambar di iPhone, menyimpannya (secara teknis: mengunduhnya, karena Safari tidak mendukung File System Access API) ke folder Download iPhone, membuka file di desktop (setelah mentransfernya dari ponsel), mengubah file, dan menimpanya dengan perubahan saya, atau bahkan menyimpannya sebagai file baru.
Contoh kode dunia nyata
Di bawah ini, Anda dapat melihat contoh browser-fs-access yang sebenarnya seperti yang digunakan di Excalidraw.
Cuplikan ini diambil dari
/src/data/json.ts
.
Yang menarik secara khusus adalah cara metode saveAsJSON()
meneruskan handle file atau null
ke metode browser-fs-access'
fileSave()
, 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 Save As selain tombol Save.
Screenshot di bawah menunjukkan perbedaan antara toolbar aplikasi utama responsif Excalidraw di iPhone dan di desktop Chrome.
Perhatikan bahwa di iPhone, tombol Save As tidak ada.
Kesimpulan
Secara teknis, bekerja dengan file sistem berfungsi pada semua browser modern. Pada browser yang mendukung File System Access API, Anda dapat membuat pengalaman ini lebih baik dengan mengizinkan penyimpanan dan penimpaan sebenarnya (tidak hanya mendownload) file, dan dengan mengizinkan pengguna membuat file baru di mana pun mereka inginkan, semuanya dengan tetap berfungsi di browser yang tidak mendukung File System Access API. browser-fs-access membuat hidup Anda lebih mudah dengan menangani seluk-beluk progressive enhancement 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 pada project ini dan karena telah meninjau Permintaan Pull saya. Banner besar oleh Ilya Pavlov di Unsplash.