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

Tarayıcılar uzun zamandır dosyalar ve dizinlerle çalışabiliyor. File API, web uygulamalarında dosya nesnelerini temsil etmenin yanı sıra bu nesneleri programatik olarak seçme ve verilerine erişme özellikleri de sunar. Ancak daha yakına baktığınızda parlayan tek şey altın değildir.

Dosyalarla başa çıkmanın geleneksel yolu

Dosyaları açma

Geliştirici olarak, <input type="file"> öğesi aracılığıyla dosyaları açıp okuyabilirsiniz. En basit haliyle, bir dosya aşağıdaki kod örneğine benzer şekilde görünebilir. input nesnesi size bir FileList verir. Aşağıdaki örnekte bu değer yalnızca bir File içerir. File spesifik bir Blob türüdür ve Blob'un yapabileceği her 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ör (veya dizin) açmak için <input webkitdirectory> özelliğini ayarlayabilirsiniz. Bunun dışında, diğer her şey yukarıdakiyle aynı şekilde çalışır. Satıcı ön ekli adına rağmen webkitdirectory yalnızca Chromium ve WebKit tarayıcılarında değil, aynı zamanda eski EdgeHTML tabanlı Edge'de ve Firefox'ta da kullanılabilir.

Dosyaları kaydetmek (yerine: indirmek)

Geleneksel olarak, dosya kaydederken yalnızca dosya indirebilirsiniz. Bu işlem, <a download> özelliği sayesinde yapılabilir. Blob verildiğinde, bağlantının href özelliğini URL.createObjectURL() yönteminden alabileceğiniz bir blob: URL'si olarak 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

İndirme yaklaşımının büyük bir dezavantajı, klasik bir açık→edit→save akışı gerçekleştirmenin, yani orijinal dosyanın üzerine yazmanın mümkün olmamasıdır. Bunun yerine, "kaydettiğinizde" her işlemde, işletim sisteminin varsayılan İndirilenler klasöründe orijinal dosyanın yeni bir kopyası elde edersiniz.

File System Access API

File System Access API hem açma hem de kaydetme işlemlerini çok daha basit hale getirir. Ayrıca, doğru kaydetmeyi de etkinleştirir. Yani yalnızca dosyanın nereye kaydedileceğini seçmekle kalmaz, aynı zamanda mevcut bir dosyanın üzerine de yazabilirsiniz.

Dosyaları açma

File System Access API ile bir dosyayı açmak için window.showOpenFilePicker() yöntemine yapılan tek bir çağrı yapılır. Bu çağrı, gerçek File değerini getFile() yöntemiyle alabileceğiniz bir dosya tanıtıcısı 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() yöntemini çağırarak bir dizin açın.

Dosyalar kaydediliyor

Dosyaları kaydetmek de benzer şekilde basittir. Dosya tanıtıcısından createWritable() aracılığıyla yazılabilir bir akış oluşturur, ardından akışın write() yöntemini çağırarak Blob verilerini yazar ve son olarak close() yöntemini çağırarak akışı kapatırsınız.

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 erişimi ile tanışın

File System Access API her ne kadar sorunsuz olsa da henüz yaygın olarak kullanılmamaktadır.

File System Access API için tarayıcı destek tablosu. Tüm tarayıcılar &quot;destek yok&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 aşamalı bir geliştirme olarak görüyorum. Bu nedenle, tarayıcı desteklediğinde bunu, desteklenmiyorsa geleneksel yaklaşımı kullanmak istiyorum. Tüm bunları yaparken kullanıcıyı desteklenmeyen JavaScript kodunu gereksiz indirmelerle asla cezalandırmamak istiyorum. Bu soruya cevabım browser-fs-access kitaplığıdır.

Tasarım felsefesi

File System Access API'nin ileride değişmesi muhtemel olduğundan, browser-fs-access API'nin modellenmesi mümkün değildir. Yani kitaplık bir polyfill değil, bir midilli dolgusu değildir. Uygulamanızı mümkün olduğunca küçük tutmak için ihtiyacınız olan tüm işlevleri (statik veya dinamik olarak) yalnızca içe aktarabilirsiniz. Uygun şekilde adlandırılmış fileOpen(), directoryOpen() ve fileSave() yöntemleri kullanılabilir. Kitaplık özelliği, dahili olarak File System Access API'nin desteklenip desteklenmediğini algılar ve ardından ilgili kod yolunu içe aktarır.

Tarayıcı fs erişim kitaplığını kullanma

Bu üç yöntemin kullanımı kolaydır. Uygulamanızın kabul edilen mimeTypes veya extensions dosyasını belirtebilir ve birden fazla dosya ya da dizinin seçilmesine izin vermek ya da vermemek için bir multiple işareti ayarlayabilirsiniz. Tüm ayrıntılar için browser-fs-access API dokümanlarına 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',
  });
})();

Demografi

Glitch'teki demoda yukarıdaki kodu çalışırken görebilirsiniz. İlgili kaynak kodu da burada bulunmaktadır. Güvenlik nedeniyle, çapraz kaynak alt çerçevelerin dosya seçici göstermesine izin verilmediğinden bu makaleye demo yerleştirilemez.

Açık tarayıcı fs-access kitaplığı

Boş zamanlarımda, elle çizilmiş bir hisle kolayca diyagramlar çizmenizi sağlayan bir beyaz tahta aracı olan Excalidraw adlı yüklenebilir bir PWA'ya küçük bir katkıda bulunuyorum. Tamamen duyarlıdır ve küçük cep telefonlarından büyük ekranlı bilgisayarlara kadar çeşitli cihazlarda iyi bir şekilde çalışır. Bu nedenle, File System Access API'yi destekleseler de desteklemeseler de tüm çeşitli platformlardaki dosyalarla ilgilenmeleri gerekir. Bu nedenle, tarayıcı-fs erişim kitaplığı için mükemmel bir adaydır.

Örneğin, iPhone'umda bir çizim başlatabilir, bunu (teknik olarak: Safari File System Access API'yi desteklemediği için indirebilir) iPhone İndirilenler klasörüme indirebilir, dosyayı masaüstümde açabilir (telefonumdan aktardıktan sonra), dosyayı değiştirebilir, değişikliklerimden üzerine yazabilir ve hatta yeni bir dosya olarak kaydedebilirim.

iPhone&#39;da bir Excalidraw çizimi.
iPhone'da File System Access API'nin desteklenmediği ancak bir dosyanın İndirilenler klasörüne kaydedilebildiği (indirilebildiği) bir Excalidraw çizimi başlatma.
Masaüstündeki Chrome&#39;daki değiştirilmiş Excalidraw çizimi.
Excalidraw çizimini File System Access API'nin desteklendiği masaüstünde açma ve değiştirme. Böylece dosyaya API üzerinden erişilebilir.
Değişiklikleri orijinal dosyanın üzerine yazmak.
Orijinal Excalidraw çizim dosyasında yapılan değişiklikleri orijinal dosyanın üzerine yazmak. Tarayıcı, bunun uygun olup olmadığını soran bir iletişim kutusu gösteriyor.
Yeni bir Excalidraw çizim dosyasında yapılan değişiklikler kaydediliyor.
Değişiklikler yeni bir Excalidraw dosyasına kaydediliyor. Orijinal dosya değişmemiştir.

Gerçek yaşam kod örneği

Aşağıda, Excalidraw'da kullanılan gerçek bir browser-fs-erişim örneği görebilirsiniz. Bu alıntı /src/data/json.ts adresinden alınmıştır. saveAsJSON() yönteminin bir dosya tanıtıcısını veya null'yi browser-fs-access' fileSave() yöntemine iletmesi özellikle önemlidir. Bu yöntem, herkese açık kullanıcı adı verildiğinde geçersiz kılınmasına veya yoksa 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 ister uygulamanızda, kullanıcı arayüzü tarayıcının destek durumuna göre uyarlanmalı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'un 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 yok olduğuna dikkat edin.

iPhone&#39;da Excalidraw uygulama araç çubuğunu yalnızca &quot;Kaydet&quot; düğmesiyle kullanabilirsiniz.
Yalnızca bir Kaydet düğmesiyle iPhone'da Excalidraw uygulama araç çubuğu.
Chrome masaüstünde, &quot;Kaydet&quot; ve &quot;Farklı Kaydet&quot; düğmeleriyle Excalidraw uygulama araç çubuğu.
Chrome'da Kaydet ve odaklanmış bir Farklı Kaydet düğmesi bulunan Excalidraw uygulama araç çubuğu.

Sonuçlar

Sistem dosyalarıyla çalışma teknik olarak tüm modern tarayıcılarda çalışır. File System Access API'yi destekleyen tarayıcılarda dosyaların gerçek bir şekilde kaydedilmesine ve üzerine yazılmasına (yalnızca indirmelerine değil) izin vererek ve kullanıcılarınızın istedikleri yerde yeni dosyalar oluşturmasına izin vererek ve File System Access API'yi desteklemeyen tarayıcılarda çalışmaya devam ederek deneyimi daha iyi hale getirebilirsiniz. browser-fs-access, progresif geliştirmenin püf noktalarını ele alarak 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 İsteklerimi inceledikleri için Excalidraw'a katkıda bulunanlara teşekkür ederim. Ilya Pavlov'un Unsplash'teki hero resmi.