ब्राउज़र-fs-access लाइब्रेरी की मदद से, फ़ाइलें और डायरेक्ट्री पढ़ना और लिखना

ब्राउज़र लंबे समय तक, फ़ाइलों और डायरेक्ट्री को मैनेज करते रहे हैं. फ़ाइल एपीआई, वेब ऐप्लिकेशन में फ़ाइल ऑब्जेक्ट को दिखाने के साथ-साथ प्रोग्राम के हिसाब से उन्हें चुनने और उनका डेटा ऐक्सेस करने की सुविधाएं देता है. हालांकि, आप जिस पल को करीब से देखते हैं, वह चमकती हुई सोने की नहीं होती.

फ़ाइलों को हल करने का पारंपरिक तरीका

फ़ाइलें खोलना

डेवलपर के तौर पर, आपके पास <input type="file"> एलिमेंट के ज़रिए फ़ाइलें खोलने और पढ़ने का विकल्प होता है. सबसे आसान रूप में, फ़ाइल को खोलने पर वह कुछ-कुछ नीचे दिए गए कोड सैंपल की तरह दिख सकता है. input ऑब्जेक्ट, आपको एक FileList देता है. नीचे दिए गए मामले में, इसमें सिर्फ़ एक File शामिल है. File एक खास तरह का Blob है. इसका इस्तेमाल किसी भी ऐसे काम के लिए किया जा सकता है जिसे 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();
  });
};

डायरेक्ट्री खोलना

फ़ोल्डर या डायरेक्ट्री खोलने के लिए, <input webkitdirectory> एट्रिब्यूट को सेट किया जा सकता है. इसके अलावा, बाकी सब कुछ ऊपर बताए गए तरीके से ही काम करता है. वेंडर-प्रीफ़िक्स नाम के बावजूद, webkitdirectory का इस्तेमाल न सिर्फ़ Chromium और WebKit ब्राउज़र में, बल्कि पुराने EdgeHTML पर आधारित Edge के साथ-साथ Firefox में भी किया जा सकता है.

फ़ाइलें सेव करना (न कि: डाउनलोड करना)

फ़ाइल सेव करने के लिए, आम तौर पर सिर्फ़ डाउनलोड किया जा सकता है. <a download> एट्रिब्यूट की मदद से ऐसा किया जा सकता है. ब्लॉब दिए जाने पर, आप ऐंकर के href एट्रिब्यूट को blob: के यूआरएल पर सेट कर सकते हैं. यह यूआरएल आपको 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();
};

समस्या

डाउनलोड करने के तरीके का एक बड़ा नुकसान यह है कि क्लासिक ओपन→एडिट→सेव करें फ़्लो को बनाने का कोई तरीका नहीं है. इसका मतलब है कि ओरिजनल फ़ाइल को ओवरराइट करने का कोई तरीका नहीं है. इसके बजाय, "सेव" करने पर आपको ऑपरेटिंग सिस्टम के डिफ़ॉल्ट डाउनलोड फ़ोल्डर में ओरिजनल फ़ाइल की एक नई कॉपी मिल जाती है.

File System Access API

File System Access API, फ़ाइलों को खोलना और सेव करना, दोनों को बहुत आसान बना देता है. इससे सही तरीके से सेव करना भी चालू हो जाता है. इसका मतलब है कि आप न सिर्फ़ फ़ाइल को सेव करने की जगह चुन सकते हैं, बल्कि मौजूदा फ़ाइल को ओवरराइट भी कर सकते हैं.

फ़ाइलें खोलना

File System Access API की मदद से, किसी फ़ाइल को खोलना, window.showOpenFilePicker() तरीके से किया जाने वाला एक कॉल है. यह कॉल एक फ़ाइल हैंडल दिखाता है, जिससे आप getFile() तरीके के ज़रिए असल File पा सकते हैं.

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

डायरेक्ट्री खोलना

window.showDirectoryPicker() को कॉल करके उस डायरेक्ट्री को खोलें जिससे फ़ाइल डायलॉग बॉक्स में, डायरेक्ट्री चुनी जा सकती हैं.

फ़ाइलें सेव की जा रही हैं

फ़ाइलें सेव करना भी उतना ही आसान है. फ़ाइल हैंडल से, createWritable() की मदद से एक ऐसी स्ट्रीम बनाई जाती है जिसमें बदलाव किया जा सकता है. इसके बाद, स्ट्रीम के write() तरीके से कॉल करके Blob डेटा लिखा जाता है और आखिर में, स्ट्रीम को बंद करने के लिए, उसके 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);
  }
};

पेश है ब्राउज़र-fs-access

File System Access API की तरह ही, यह अभी तक बड़े पैमाने पर उपलब्ध नहीं है.

File System Access API के लिए, ब्राउज़र सहायता टेबल. सभी ब्राउज़र &#39;कोई सहायता नहीं&#39; या &#39;फ़्लैग के पीछे&#39; के तौर पर मार्क किए जाते हैं.
File System Access API के लिए, ब्राउज़र सहायता टेबल. (सोर्स)

इसी वजह से मुझे File System Access API, प्रोग्रेसिव एन्हैंसमेंट के तौर पर दिखता है. इसलिए, मैं इसका इस्तेमाल तब करना चाहूंगी जब ब्राउज़र पर यह काम करे. अगर ऐसा नहीं है, तो मैं पारंपरिक तरीके का ही इस्तेमाल करना चाहूँगी. इस चुनौती में, ब्राउज़र-fs-access लाइब्रेरी का इस्तेमाल किया जा सकता है.

डिज़ाइन दर्शन

आने वाले समय में, File System Access API में बदलाव हो सकता है. इसलिए, इसके बाद ब्राउज़र-fs-access API को मॉडल नहीं किया जाता. इसका मतलब है कि लाइब्रेरी कोई polyfill नहीं, बल्कि एक पोनीफ़िल है. अपने ऐप्लिकेशन को छोटा रखने के लिए, ज़रूरत के हिसाब से (स्टैटिक या डाइनैमिक तौर पर) खास तौर पर इंपोर्ट किया जा सकता है. मौजूद तरीकों के नाम हैं fileOpen(), directoryOpen(), और fileSave(). अंदरूनी तौर पर, लाइब्रेरी यह पता लगाती है कि फ़ाइल सिस्टम ऐक्सेस एपीआई काम करता है या नहीं. इसके बाद, लाइब्रेरी उससे जुड़े कोड पाथ को इंपोर्ट करती है.

ब्राउज़र-fs-access लाइब्रेरी का इस्तेमाल करना

इन तीनों तरीकों का इस्तेमाल करना आसान है. अपने ऐप्लिकेशन के लिए स्वीकार किए जाने वाले mimeTypes या फ़ाइल extensions की जानकारी दें. साथ ही, एक से ज़्यादा फ़ाइलों या डायरेक्ट्री को चुनने की अनुमति देने या न देने के लिए, multiple फ़्लैग सेट करें. पूरी जानकारी के लिए, ब्राउज़र-fs-access API के दस्तावेज़ देखें. नीचे दिया गया कोड सैंपल दिखाता है कि इमेज फ़ाइलों को कैसे खोला और सेव किया जा सकता है.

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

डेमो

ऊपर दिए गए कोड को Glitch पर, डेमो में देखा जा सकता है. इसका सोर्स कोड भी उसी तरह वहां उपलब्ध होता है. सुरक्षा वजहों से क्रॉस ऑरिजिन सब फ़्रेम को फ़ाइल पिकर दिखाने की अनुमति नहीं है, इसलिए इस लेख में डेमो एम्बेड नहीं किया जा सकता.

जंगल में मौजूद ब्राउज़र-fs-access की लाइब्रेरी

खाली समय में, मैंने Excalidraw नाम के इंस्टॉल किए जा सकने वाले PWA को बनाने के लिए एक छोटा सा योगदान दिया है. यह एक व्हाइटबोर्ड टूल है जिसकी मदद से, हाथ से बनाए गए डायग्राम के साथ डायग्राम आसानी से स्केच किए जा सकते हैं. यह पूरी तरह से रिस्पॉन्सिव है और छोटे मोबाइल फ़ोन से लेकर बड़ी स्क्रीन वाले कंप्यूटर तक, कई तरह के डिवाइसों पर अच्छी तरह से काम करता है. इसका मतलब है कि इसे अलग-अलग प्लैटफ़ॉर्म पर मौजूद फ़ाइलों से निपटने की ज़रूरत है, चाहे वे प्लैटफ़ॉर्म File System Access API के साथ काम करते हों या नहीं. इससे यह ब्राउज़र-fs-access लाइब्रेरी के लिए एक अच्छा विकल्प बन जाता है.

उदाहरण के लिए, मैं अपने iPhone पर ड्रॉइंग शुरू कर सकता/सकती हूं, इसे (तकनीकी तौर पर: डाउनलोड कर सकता/सकती हूं, क्योंकि Safari फ़ाइल सिस्टम ऐक्सेस एपीआई का समर्थन नहीं करता) मेरे iPhone में डाउनलोड फ़ोल्डर में कर सकता/सकती हूं, फ़ाइल को मेरे डेस्कटॉप पर खोल सकता/सकती हूं (मेरे फ़ोन से ट्रांसफ़र करने के बाद), फ़ाइल में बदलाव कर सकता/सकती हूं, मेरे बदलावों से ओवरराइट कर सकता/सकती हूं या उसे एक नई फ़ाइल के रूप में सेव कर सकता/सकती हूं.

iPhone पर एक Excalidरॉ ड्रॉइंग.
किसी ऐसे iPhone पर Excalidraw ड्रॉइंग शुरू करना जहां फ़ाइल सिस्टम ऐक्सेस एपीआई काम नहीं करता, लेकिन डाउनलोड फ़ोल्डर में कोई फ़ाइल कहां सेव (डाउनलोड) की जा सकती है.
डेस्कटॉप पर, Chrome में बदलाव की गई Excalidraw ड्रॉइंग.
उस डेस्कटॉप पर Excalidraw ड्रॉइंग खोलना और उसमें बदलाव करना जिस पर File System Access API काम करता है. इसकी वजह से, फ़ाइल को एपीआई के ज़रिए ऐक्सेस किया जा सकता है.
बदलावों के साथ मूल फ़ाइल को ओवरराइट करें.
ओरिजनल फ़ाइल की ड्रॉइंग फ़ाइल में बदलाव करके, ओरिजनल फ़ाइल को ओवरराइट करना. ब्राउज़र एक डायलॉग दिखाता है, जिसमें मुझसे यह पूछा जाता है कि यह ठीक है या नहीं.
बदलावों को नई Excalidraw ड्रॉइंग फ़ाइल में सेव किया जा रहा है.
बदलावों को नई Excalidraw फ़ाइल में सेव किया जा रहा है. ओरिजनल फ़ाइल में कोई बदलाव नहीं होता.

रीयल लाइफ़ कोड सैंपल

नीचे, ब्राउज़र-fs-access का एक उदाहरण दिया गया है, क्योंकि Excalidraw में इसका इस्तेमाल किया जाता है. यह हिस्सा /src/data/json.ts से लिया गया है. खास बात यह है कि saveAsJSON() वाला तरीका, फ़ाइल हैंडल या null को ब्राउज़र-fs-access'fileSave() तरीके में पास करता है. इसकी वजह से, हैंडल दिए जाने पर यह तरीका ओवरराइट हो जाता है या न होने पर, उसे नई फ़ाइल में सेव कर दिया जाता है.

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

यूज़र इंटरफ़ेस (यूआई) के बारे में जानकारी

चाहे Excalidraw हो या आपके ऐप्लिकेशन, यूआई को ब्राउज़र की सहायता स्थिति के मुताबिक होना चाहिए. अगर File System Access API, (if ('showOpenFilePicker' in window) {}) के साथ काम करता है, तो सेव करें बटन के साथ-साथ, इस रूप में सेव करें बटन भी दिखाया जा सकता है. नीचे दिए गए स्क्रीनशॉट, iPhone और Chrome डेस्कटॉप पर, Excalidraw के रिस्पॉन्सिव मुख्य ऐप्लिकेशन टूलबार के बीच का अंतर दिखाते हैं. ध्यान दें कि कैसे iPhone में इस रूप में सेव करें बटन उपलब्ध नहीं है.

बस &#39;सेव करें&#39; बटन से iPhone पर Excalidraw ऐप्लिकेशन टूलबार.
iPhone पर, सिर्फ़ सेव करें बटन से, iPhone पर Excalidraw ऐप्लिकेशन टूलबार.
Chrome डेस्कटॉप पर, &#39;सेव करें&#39; और &#39;इस रूप में सेव करें&#39; बटन के साथ ऐप्लिकेशन टूलबार का पेज एक्सकैलिड्रॉल.
Chrome पर, ऐप्लिकेशन के टूलबार को सेव करें और फ़ोकस किए गए इस रूप में सेव करें बटन से, ऐप्लिकेशन टूलबार का पेज लोड करें.

मीटिंग में सामने आए नतीजे

सिस्टम फ़ाइलों के साथ काम करना तकनीकी रूप से सभी मॉडर्न ब्राउज़र पर काम करता है. File System Access API के साथ काम करने वाले ब्राउज़र में, फ़ाइलों को सही तरीके से सेव और ओवरराइट (सिर्फ़ डाउनलोड करने) की अनुमति देकर और उपयोगकर्ताओं को जहां चाहे वहां से नई फ़ाइलें बनाने की अनुमति देकर बेहतर अनुभव दिया जा सकता है. इससे, यह कुछ उन ब्राउज़र में काम करते हुए किया जा सकता है जो File System Access API के साथ काम नहीं करते. ब्राउज़र-fs-access की मदद से, प्रोग्रेस को बेहतर बनाने की बारीकियों का ध्यान रखें और अपने कोड को जितना हो सके उतना आसान बनाएं.

स्वीकार हैं

इस लेख की समीक्षा जो मेडली और केएस बास्क ने की है. प्रोजेक्ट पर काम करने और मेरे पुल के अनुरोधों की समीक्षा करने के लिए, Excalidraw के योगदान देने वालों को धन्यवाद. Unsplash पर इलया पावलोव की हीरो इमेज.