Tarayıcı-fs erişim kitaplığı ile dosya ve dizinleri okuma ve yazma

Tarayıcılar, uzun zamandır dosya ve dizinlerle işlem yapmaktaydı. File API, web uygulamalarında dosya nesnelerini temsil etmenin yanı sıra bunları programatik olarak seçmek ve verilerine erişmek için özellikler sağlar. Ancak daha yakından baktığınızda her şeyin göründüğü gibi olmadığını görebilirsiniz.

Dosyaları açma

Geliştirici olarak, dosyaları <input type="file"> öğesi aracılığıyla açıp okuyabilirsiniz. Bir dosyayı açma işleminin en basit hali aşağıdaki kod örneğine benzer şekilde görünebilir. input nesnesi size bir FileList verir. Aşağıdaki örnekte bu nesne yalnızca bir File içerir. File, belirli bir Blob türüdür ve Blob'un yapabileceği herhangi bir bağlamda kullanılabilir.

const openFile = async () => {
  return new Promise((resolve) => {
    const input = document.createElement('input');
    input.type = 'file';
    input.addEventListener('change', () => {
      resolve(input.files[0]);
    });
    input.click();
  });
};

Dizinleri açma

Klasörleri (veya dizinleri) açmak için <input webkitdirectory> özelliğini ayarlayabilirsiniz. Bunun dışında, diğer her şey yukarıdaki gibi çalışır. Satıcı önekli adına rağmen webkitdirectory yalnızca Chromium ve WebKit tarayıcılarda değil, aynı zamanda Firefox'un yanı sıra EdgeHTML tabanlı Edge'de de kullanılabilir.

Dosyaları kaydetme (doğrusu: indirme)

Dosya kaydetmek için geleneksel olarak indirme seçeneğiyle sınırlısınız. Bu seçenek, <a download> özelliği sayesinde kullanılabilir. Bir Blob verildiğinde, ankrajın href özelliğini URL.createObjectURL() yönteminden alabileceğiniz bir blob: URL'sine ayarlayabilirsiniz.

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();
};

Sorun

İndir yaklaşımının en büyük dezavantajı, klasik bir açma→düzenleme→kaydetme akışı oluşturamamasıdır. Yani orijinal dosyanın üzerinin silinmesi mümkün değildir. Bunun yerine, "kaydettiğimizde" işletim sisteminin varsayılan İndirilenler klasöründe orijinal dosyanın yeni bir kopyası oluşturulur.

File System Access API

File System Access API her iki işlemi de (açma ve kaydetme) çok daha basit hale getirir. Ayrıca gerçek kaydetme özelliğini de etkinleştirir. Yani dosyayı nereye kaydedeceğinizi seçmekle kalmaz, mevcut bir dosyanın üzerine de yazabilirsiniz.

Dosya açma

File System Access API ile dosya açma işlemi, window.showOpenFilePicker() yöntemine tek bir çağrıda bulunarak gerçekleştirilebilir. Bu çağrı, getFile() yöntemini kullanarak gerçek File değerini alabileceğiniz bir dosya herkese açık kullanıcı adı döndürür.

const openFile = async () => {
  try {
    // Always returns an array.
    const [handle] = await window.showOpenFilePicker();
    return handle.getFile();
  } catch (err) {
    console.error(err.name, err.message);
  }
};

Dizinleri açma

Dosya iletişim kutusunda dizinleri seçilebilir hale getiren window.showDirectoryPicker() işlevini çağırarak bir dizin açın.

Dosya kaydetme

Dosya kaydetme işlemi de benzer şekilde kolaydır. Bir dosya işleyiciden, createWritable() aracılığıyla yazılabilir bir akış oluşturuyorsunuz, ardından akışın write() yöntemini çağırarak Blob verilerini yazıyorsunuz ve son olarak, close() yöntemini çağırarak akışı kapatıyorsunuz.

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);
  }
};

Tarayıcı-fs-access ile tanışın

File System Access API mükemmel bir API olsa da henüz yaygın olarak kullanılamamaktadır.

File System Access API için tarayıcı desteği tablosu. Tüm tarayıcılar &quot;desteklenmiyor&quot; veya &quot;bayrağın arkasında&quot; olarak işaretlenir.
File System Access API için tarayıcı desteği tablosu. (Kaynak)

Bu nedenle, File System Access API'yi kademeli bir geliştirme olarak görüyorum. Bu nedenle, tarayıcı desteklediğinde bu yöntemi, desteklemediğinde ise geleneksel yaklaşımı kullanmak istiyorum. Bu süreçte, kullanıcıyı desteklenmeyen JavaScript kodlarının gereksiz indirmeleriyle cezalandırmak istemiyorum. browser-fs-access kitaplığı, bu soruna verdiğim yanıttır.

Tasarım felsefesi

File System Access API'nin gelecekte değişme olasılığı olduğundan browser-fs-access API bu API'ye göre modellenmemiştir. Yani kütüphane bir polyfill değil, ponyfill'dir. Uygulamanızı olabildiğince küçük tutmak için ihtiyacınız olan işlevleri yalnızca (statik veya dinamik olarak) içe aktarabilirsiniz. Kullanılabilir yöntemler, fileOpen(), directoryOpen() ve fileSave() olarak adlandırılmıştır. Kitaplık özelliği dahili olarak File System Access API'nin desteklenip desteklenmediğini algılar ve ilgili kod yolunu içe aktarır.

browser-fs-access kitaplığını kullanma

Bu üç yöntem kolayca kullanılabilir. Uygulamanızda kabul edilen mimeTypes veya extensions dosyasını belirtebilir ve birden fazla dosya ya da dizin seçilmesine izin vermek veya vermemek için multiple işareti ayarlayabilirsiniz. Tüm ayrıntılar için browser-fs-access API belgelerine bakın. Aşağıdaki kod örneğinde, resim dosyalarını nasıl açıp kaydedebileceğiniz gösterilmektedir.

// 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

Yukarıdaki kodun işleyişini Glitch'teki demo'da görebilirsiniz. Kaynak kodu da buradan edinilebilir. Güvenlik nedeniyle kaynakta çapraz alt çerçevelerin dosya seçici göstermesine izin verilmediğinden demo bu makaleye yerleştirilemez.

Kullanımdaki browser-fs-access kitaplığı

Boş zamanlarımda, Excalidraw adlı yüklenebilir bir PWA'ya katkıda bulunuyorum. Bu beyaz tahta aracı, el çizimi hissi veren diyagramları kolayca çizmenize olanak tanır. Tamamen duyarlıdır ve küçük cep telefonlarından büyük ekranlı bilgisayarlara kadar pek çok farklı cihazda iyi bir şekilde çalışır. Bu nedenle, File System Access API'yi destekleyip desteklemediklerinden bağımsız olarak çeşitli platformlardaki dosyalarla ilgilenmesi gerekir. Bu, browser-fs-access kitaplığı için mükemmel bir aday yapar.

Örneğin, iPhone'umda bir çizime başlıyor, çizimi iPhone'daki İndirilenler klasörüne kaydedebilir (teknik olarak onu indirebilirim (çünkü Safari, File System Access API'yi desteklemediğinden), dosyayı masaüstümde açabilir (telefonumdan aktardıktan sonra), dosyayı değiştirebilir, yaptığım değişikliklerin üzerine yazabilir ve hatta yeni bir dosya olarak kaydedebilirim.

iPhone&#39;da Excalidraw çizimi.
File System Access API'nin desteklenmediği ancak bir dosyanın İndirilenler klasörüne kaydedilebileceği (indirilebileceği) bir iPhone'da Excalidraw çizimi başlatma.
Masaüstünde Chrome&#39;da değiştirilmiş Excalidraw çizimi.
File System Access API'nin desteklendiği ve dolayısıyla dosyaya API üzerinden erişilebildiği masaüstünde Excalidraw çizimini açma ve değiştirme.
Orijinal dosyanın üzerine değişikliklerin yazılmasıdır.
Orijinal Excalidraw çizim dosyasındaki değişiklikleri yaparak orijinal dosyanın üzerine yazmak. Tarayıcıda, bunun uygun olup olmadığını soran bir iletişim kutusu gösteriliyor.
Değişiklikleri yeni bir Excalidraw çizim dosyasına kaydetme.
Yeni bir Excalidraw dosyasında yapılan değişiklikler kaydediliyor. Orijinal dosyaya dokunulmaz.

Gerçek hayattan kod örneği

Aşağıda, Excalidraw'da kullanılan gerçek bir browser-fs-access örneğini görebilirsiniz. Bu alıntı /src/data/json.ts sitesinden alınmıştır. saveAsJSON() yönteminin, bir dosya işleyiciyi veya null yöntemini browser-fs-access' fileSave() yöntemine iletme şekli özellikle ilgi çekicidir. Bu da, herkese açık kullanıcı adı verildiğinde üzerine yazılmasına veya sağlanmamışsa yeni bir dosyaya kaydedilmesine neden olur.

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);
};

Kullanıcı arayüzüyle ilgili dikkat edilmesi gereken noktalar

İster Excalidraw'da ister uygulamanızda, kullanıcı arayüzü, tarayıcının destek durumuna uyum sağlamalıdır. File System Access API destekleniyorsa (if ('showOpenFilePicker' in window) {}) Kaydet düğmesine ek olarak Farklı Kaydet düğmesi de gösterebilirsiniz. Aşağıdaki ekran görüntülerinde, Excalidraw'ın iPhone'daki ve Chrome masaüstündeki duyarlı ana uygulama araç çubuğu arasındaki fark gösterilmektedir. iPhone'da Farklı Kaydet düğmesinin nasıl görünmediğine dikkat edin.

iPhone&#39;da yalnızca &quot;Kaydet&quot; düğmesi bulunan Excalidraw uygulama araç çubuğu.
iPhone'da yalnızca bir Kaydet düğmesi bulunan Excalidraw uygulama araç çubuğu.
Chrome masaüstünde &quot;Kaydet&quot; ve &quot;Farklı Kaydet&quot; düğmesi bulunan Excalidraw uygulama araç çubuğu.
Chrome'daki Kaydet ve odaklanmış Başka Adı Olarak Kaydet düğmesi bulunan Excalidraw uygulama araç çubuğu.

Sonuçlar

Sistem dosyalarıyla çalışmak teknik olarak tüm modern tarayıcılarda çalışır. File System Access API'yi destekleyen tarayıcılarda, dosyaların gerçek anlamda kaydedilmesine ve üzerine yazılmasına (yalnızca indirilmesine değil) izin vererek ve kullanıcılarınızın istedikleri yerde yeni dosya oluşturmasına izin vererek deneyimi iyileştirebilir, File System Access API'yi desteklemeyen tarayıcılarda ise işlevselliğini koruyabilirsiniz. browser-fs-access, progresif geliştirmenin incelikleriyle uğraşarak ve kodunuzu mümkün olduğunca basit hale getirerek hayatınızı kolaylaştırır.

Teşekkür

Bu makale Joe Medley ve Kayce Basques tarafından incelenmiştir. Projedeki çalışmaları ve çekme isteklerimizi incelemeleri için Excalidraw'a katkıda bulunanlara teşekkür ederiz. Unsplash'taki Ilya Pavlov tarafından oluşturulan lokomotif resim.