फ़ाइलों और डायरेक्ट्री को पढ़ने और उनमें बदलाव करने का ऐक्सेस

पब्लिश होने की तारीख: 27 जुलाई, 2020

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

फ़ाइलों को मैनेज करने का पारंपरिक तरीका

फ़ाइलें खोलो

<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> एट्रिब्यूट की वजह से काम करता है. किसी BLOB के लिए, ऐंकर के 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() तरीके को एक बार कॉल करके किसी फ़ाइल को खोला जा सकता है. इस कॉल से फ़ाइल हैंडल मिलता है. इससे File तरीके का इस्तेमाल करके, असली File पाया जा सकता है.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);
  }
};

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

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

पेश है browser-fs-access

File System Access API बहुत अच्छा है, लेकिन यह अभी ज़्यादातर लोगों के लिए उपलब्ध नहीं है.

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

इसलिए, मुझे फ़ाइल सिस्टम ऐक्सेस एपीआई, प्रोग्रेसिव एन्हांसमेंट के तौर पर दिखता है. इसलिए, मैं इसका इस्तेमाल तब करना चाहता हूं, जब ब्राउज़र इसे सपोर्ट करता हो. अगर ऐसा नहीं होता है, तो मैं पारंपरिक तरीके का इस्तेमाल करना चाहता हूं. साथ ही, मैं यह भी चाहता हूं कि उपयोगकर्ता को ऐसे JavaScript कोड को डाउनलोड करने के लिए मजबूर न किया जाए जो काम नहीं करता. browser-fs-access लाइब्रेरी, इस चुनौती का जवाब है.

डिज़ाइन फ़िलॉसफ़ी

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

लाइब्रेरी का इस्तेमाल करना

इन तीनों तरीकों को इस्तेमाल करना आसान है. आपके पास अपने ऐप्लिकेशन के लिए, स्वीकार किए गए mimeTypes या फ़ाइल extensions के बारे में बताने का विकल्प होता है. साथ ही, एक से ज़्यादा फ़ाइलों या डायरेक्ट्री को चुनने की अनुमति देने या न देने के लिए, multiple फ़्लैग सेट किया जा सकता है. पूरी जानकारी के लिए, browser-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',
  });
})();

डेमो

इस कोड को GitHub डेमो में इस्तेमाल करके देखा जा सकता है. इसका सोर्स कोड भी वहां उपलब्ध है.

browser-fs-access लाइब्रेरी का इस्तेमाल

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

उदाहरण के लिए, मैं अपने iPhone पर कोई ड्राइंग शुरू कर सकता/सकती हूं. इसके बाद, उसे सेव कर सकता/सकती हूं. तकनीकी तौर पर, इसे डाउनलोड करना कहा जाता है, क्योंकि Safari, File System Access API के साथ काम नहीं करता. इसके बाद, मैं उसे iPhone के डाउनलोड फ़ोल्डर में सेव कर सकता/सकती हूं. इसके बाद, मैं उस फ़ाइल को अपने डेस्कटॉप पर खोल सकता/सकती हूं. इसके लिए, मुझे उसे अपने फ़ोन से डेस्कटॉप पर ट्रांसफ़र करना होगा. इसके बाद, मैं उस फ़ाइल में बदलाव कर सकता/सकती हूं और उसे अपने बदलावों के साथ सेव कर सकता/सकती हूं. इसके अलावा, मैं उसे नई फ़ाइल के तौर पर भी सेव कर सकता/सकती हूं.

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

असल ज़िंदगी का कोड सैंपल

नीचे, Excalidraw में इस्तेमाल किए गए browser-fs-access का एक उदाहरण दिया गया है. यह जवाब, /src/data/json.ts से लिया गया है. खास तौर पर, यह देखना ज़रूरी है कि saveAsJSON() फ़ंक्शन, फ़ाइल हैंडल या null को browser-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 पर Save As बटन मौजूद नहीं है.

iPhone पर Excalidraw ऐप्लिकेशन का टूलबार. इसमें सिर्फ़ &#39;सेव करें&#39; बटन है.
iPhone पर Excalidraw ऐप्लिकेशन का टूलबार. इसमें सिर्फ़ Save बटन है.
Chrome पर Excalidraw ऐप्लिकेशन का टूलबार. इसमें सेव करें और इस नाम से सेव करें बटन को हाइलाइट किया गया है.

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

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

Acknowledgements

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