File System Access API: लोकल फ़ाइलों का ऐक्सेस आसान बनाना

File System Access API की मदद से, वेब ऐप्लिकेशन सीधे तौर पर उपयोगकर्ता के डिवाइस पर मौजूद फ़ाइलों और फ़ोल्डर में बदलावों को पढ़ सकते हैं या सेव कर सकते हैं.

File System Access API क्या है?

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

अगर आपने पहले फ़ाइलों को पढ़ने और उनमें बदलाव करने के बारे में पढ़ा है, तो आपको यहां बताई गई बातों के बारे में पता होगा. हमारा सुझाव है कि आप इसे पढ़ें, क्योंकि सभी सिस्टम एक जैसे नहीं होते.

फ़ाइल सिस्टम ऐक्सेस एपीआई, Windows, macOS, ChromeOS, और Linux पर काम करने वाले ज़्यादातर Chromium ब्राउज़र पर काम करता है. हालांकि, Brave ब्राउज़र में यह सुविधा फ़िलहाल सिर्फ़ फ़्लैग के पीछे उपलब्ध है. Android के लिए, crbug.com/1011535 के संदर्भ में काम किया जा रहा है.

File System Access API का इस्तेमाल करना

फ़ाइल सिस्टम ऐक्सेस एपीआई की क्षमता और फ़ायदों को दिखाने के लिए, मैंने एक फ़ाइल टेक्स्ट एडिटर लिखा है. इसकी मदद से, टेक्स्ट फ़ाइल खोली जा सकती है, उसमें बदलाव किया जा सकता है, और बदलावों को डिस्क पर सेव किया जा सकता है. इसके अलावा, नई फ़ाइल बनाई जा सकती है और उसमें किए गए बदलावों को डिस्क पर सेव किया जा सकता है. यह कोई खास सुविधा नहीं है, लेकिन इसमें आपको कॉन्सेप्ट समझने के लिए ज़रूरी जानकारी मिलती है.

ब्राउज़र समर्थन

ब्राउज़र सहायता

  • Chrome: 86.
  • Edge: 86.
  • Firefox: समर्थित नहीं.
  • Safari: यह सुविधा काम नहीं करती.

सोर्स

फ़ीचर का पता लगाना

यह जानने के लिए कि फ़ाइल सिस्टम ऐक्सेस एपीआई काम करता है या नहीं, देखें कि आपको जिस पिकर तरीके में अपनी दिलचस्पी है वह मौजूद है या नहीं.

if ('showOpenFilePicker' in self) {
  // The `showOpenFilePicker()` method of the File System Access API is supported.
}

इसे आज़माएं

टेक्स्ट एडिटर के डेमो में, File System Access API को काम करते हुए देखें.

डिवाइस के फ़ाइल सिस्टम से फ़ाइल पढ़ना

पहला इस्तेमाल का उदाहरण, उपयोगकर्ता से कोई फ़ाइल चुनने के लिए कहना है. इसके बाद, डिस्क से उस फ़ाइल को खोलना और पढ़ना है.

पढ़ने के लिए उपयोगकर्ता को कोई फ़ाइल चुनने के लिए कहें

File System Access API का एंट्री पॉइंट window.showOpenFilePicker() है. कॉल करने पर, यह फ़ाइल पिकर डायलॉग दिखाता है और उपयोगकर्ता को कोई फ़ाइल चुनने के लिए कहता है. फ़ाइल चुनने के बाद एपीआई, फ़ाइल हैंडल की कलेक्शन दिखाता है. options पैरामीटर की मदद से, फ़ाइल पिकर के व्यवहार को कंट्रोल किया जा सकता है. उदाहरण के लिए, उपयोगकर्ता को एक से ज़्यादा फ़ाइलें, डायरेक्ट्री या अलग-अलग फ़ाइल टाइप चुनने की अनुमति देकर. फ़ाइल पिकर में कोई विकल्प तय किए बिना, उपयोगकर्ता को एक फ़ाइल चुनने की अनुमति मिलती है. यह टेक्स्ट एडिटर के लिए सबसे सही है.

कई अन्य असरदार एपीआई की तरह, showOpenFilePicker() को भी सुरक्षित जानकारी में कॉल किया जाना चाहिए. साथ ही, उपयोगकर्ता के जेस्चर में ही कॉल किया जाना चाहिए.

let fileHandle;
butOpenFile.addEventListener('click', async () => {
  // Destructure the one-element array.
  [fileHandle] = await window.showOpenFilePicker();
  // Do something with the file handle.
});

उपयोगकर्ता किसी फ़ाइल को चुनने के बाद, showOpenFilePicker() हैंडल का कलेक्शन दिखाता है. इस मामले में, एक FileSystemFileHandle वाला एक-एलिमेंट कलेक्शन दिखाता है. इसमें फ़ाइल के साथ इंटरैक्ट करने के लिए ज़रूरी प्रॉपर्टी और तरीके होते हैं.

फ़ाइल हैंडल का रेफ़रंस रखना मददगार होता है, ताकि बाद में उसका इस्तेमाल किया जा सके. फ़ाइल में बदलावों को सेव करने या फ़ाइल से जुड़ी कोई अन्य कार्रवाई करने के लिए, यह ज़रूरी है.

फ़ाइल सिस्टम से फ़ाइल पढ़ना

अब आपके पास फ़ाइल का हैंडल है, इसलिए आपके पास फ़ाइल की प्रॉपर्टी पाने या फ़ाइल को ऐक्सेस करने का विकल्प है. फ़िलहाल, मैं इसका कॉन्टेंट पढ़ूँगी. handle.getFile() को कॉल करने पर, एक File ऑब्जेक्ट दिखता है. इसमें एक ब्लॉब होता है. ब्लॉब से डेटा पाने के लिए, इसके किसी एक तरीके (slice(), stream(), text() या arrayBuffer()) को कॉल करें.

const file = await fileHandle.getFile();
const contents = await file.text();

FileSystemFileHandle.getFile() से मिले File ऑब्जेक्ट को सिर्फ़ तब तक पढ़ा जा सकता है, जब तक डिस्क पर मौजूद फ़ाइल में कोई बदलाव नहीं होता. अगर डिस्क पर मौजूद फ़ाइल में बदलाव किया जाता है, तो File ऑब्जेक्ट को पढ़ा नहीं जा सकता. साथ ही, बदले गए डेटा को पढ़ने के लिए, आपको नया File ऑब्जेक्ट पाने के लिए getFile() को फिर से कॉल करना होगा.

यह रही पूरी जानकारी

जब उपयोगकर्ता खोलें बटन पर क्लिक करते हैं, तो ब्राउज़र एक फ़ाइल पिकर दिखाता है. फ़ाइल चुनने के बाद, ऐप्लिकेशन उसका कॉन्टेंट पढ़कर उसे <textarea> में डाल देता है.

let fileHandle;
butOpenFile.addEventListener('click', async () => {
  [fileHandle] = await window.showOpenFilePicker();
  const file = await fileHandle.getFile();
  const contents = await file.text();
  textArea.value = contents;
});

फ़ाइल को लोकल फ़ाइल सिस्टम में लिखना

टेक्स्ट एडिटर में, फ़ाइल को सेव करने के दो तरीके हैं: सेव करें और इस रूप में सेव करें. सेव करें पर क्लिक करके, बदलावों को मूल फ़ाइल में वापस लिखा जाता है. इसके लिए, पहले मिले फ़ाइल हैंडल का इस्तेमाल किया जाता है. हालांकि, इसके तौर पर सेव करें से नई फ़ाइल बनती है. इसलिए, इसके लिए नए फ़ाइल हैंडल की ज़रूरत होती है.

नई फ़ाइल बनाना

किसी फ़ाइल को सेव करने के लिए, showSaveFilePicker() को कॉल करें. इससे, फ़ाइल पिकर "सेव करें" मोड में दिखता है. इससे उपयोगकर्ता, नई फ़ाइल चुन सकता है और उसे सेव कर सकता है. टेक्स्ट एडिटर के लिए, मुझे यह भी चाहिए था कि वह .txt एक्सटेंशन अपने-आप जोड़ दे. इसलिए, मैंने कुछ और पैरामीटर दिए.

async function getNewFileHandle() {
  const options = {
    types: [
      {
        description: 'Text Files',
        accept: {
          'text/plain': ['.txt'],
        },
      },
    ],
  };
  const handle = await window.showSaveFilePicker(options);
  return handle;
}

डिस्क पर बदलाव सेव करना

किसी फ़ाइल में किए गए बदलावों को सेव करने के लिए, GitHub पर मेरे टेक्स्ट एडिटर के डेमो में मौजूद सभी कोड देखे जा सकते हैं. फ़ाइल सिस्टम के मुख्य इंटरैक्शन, fs-helpers.js में मौजूद हैं. आसान शब्दों में, यह प्रोसेस नीचे दिए गए कोड की तरह दिखती है. मैं आपको हर चरण पर बताऊंगी और इसके बारे में विस्तार से बताऊंगी.

// fileHandle is an instance of FileSystemFileHandle..
async function writeFile(fileHandle, contents) {
  // Create a FileSystemWritableFileStream to write to.
  const writable = await fileHandle.createWritable();
  // Write the contents of the file to the stream.
  await writable.write(contents);
  // Close the file and write the contents to disk.
  await writable.close();
}

डिस्क पर डेटा लिखने के लिए, FileSystemWritableFileStream ऑब्जेक्ट का इस्तेमाल किया जाता है. यह WritableStream का सबक्लास होता है. फ़ाइल हैंडल ऑब्जेक्ट पर createWritable() को कॉल करके स्ट्रीम बनाएं. createWritable() को कॉल करने पर, ब्राउज़र सबसे पहले यह जांच करता है कि उपयोगकर्ता ने फ़ाइल में बदलाव करने की अनुमति दी है या नहीं. अगर लिखने की अनुमति नहीं दी गई है, तो ब्राउज़र, उपयोगकर्ता से अनुमति मांगने का अनुरोध करता है. अगर अनुमति नहीं दी जाती है, तो createWritable() एक DOMException दिखाता है और ऐप्लिकेशन फ़ाइल में डेटा नहीं लिख पाएगा. टेक्स्ट एडिटर में, DOMException ऑब्जेक्ट को saveFile() तरीके से मैनेज किया जाता है.

write() तरीका, एक स्ट्रिंग लेता है. टेक्स्ट एडिटर के लिए इसकी ज़रूरत होती है. हालांकि, इसमें BufferSource या Blob भी इस्तेमाल किया जा सकता है. उदाहरण के लिए, किसी स्ट्रीम को सीधे उससे पाइप किया जा सकता है:

async function writeURLToFile(fileHandle, url) {
  // Create a FileSystemWritableFileStream to write to.
  const writable = await fileHandle.createWritable();
  // Make an HTTP request for the contents.
  const response = await fetch(url);
  // Stream the response into the file.
  await response.body.pipeTo(writable);
  // pipeTo() closes the destination pipe by default, no need to close it.
}

फ़ाइल को किसी खास जगह पर अपडेट करने या फ़ाइल का साइज़ बदलने के लिए, स्ट्रीम में seek() या truncate() भी दिया जा सकता है.

फ़ाइल का सुझाया गया नाम और स्टार्ट डायरेक्ट्री तय करना

कई मामलों में हो सकता है कि आप अपने ऐप्लिकेशन से किसी डिफ़ॉल्ट फ़ाइल नाम या जगह का सुझाव देना चाहें. उदाहरण के लिए, हो सकता है कि कोई टेक्स्ट एडिटर Untitled के बजाय Untitled Text.txt के डिफ़ॉल्ट फ़ाइल नाम का सुझाव देना चाहे. showSaveFilePicker विकल्पों के हिस्से के तौर पर suggestedName प्रॉपर्टी पास करके, ऐसा किया जा सकता है.

const fileHandle = await self.showSaveFilePicker({
  suggestedName: 'Untitled Text.txt',
  types: [{
    description: 'Text documents',
    accept: {
      'text/plain': ['.txt'],
    },
  }],
});

डिफ़ॉल्ट स्टार्ट डायरेक्ट्री के लिए भी यही बात लागू होती है. अगर कोई टेक्स्ट एडिटर बनाया जा रहा है, तो हो सकता है कि आप डिफ़ॉल्ट documents फ़ोल्डर में फ़ाइल सेव करने या फ़ाइल खोलने का डायलॉग शुरू करना चाहें. वहीं, इमेज एडिटर के लिए, डिफ़ॉल्ट pictures फ़ोल्डर में शुरू किया जा सकता है. showSaveFilePicker, showDirectoryPicker() या showOpenFilePicker तरीकों में startIn प्रॉपर्टी को पास करके, डिफ़ॉल्ट स्टार्ट डायरेक्ट्री का सुझाव दिया जा सकता है.

const fileHandle = await self.showOpenFilePicker({
  startIn: 'pictures'
});

जानी-मानी सिस्टम डायरेक्ट्री की सूची यहां दी गई है:

  • desktop: उपयोगकर्ता की डेस्कटॉप डायरेक्ट्री, अगर ऐसी कोई डायरेक्ट्री मौजूद है.
  • documents: वह डायरेक्ट्री जिसमें आम तौर पर उपयोगकर्ता के बनाए गए दस्तावेज़ सेव किए जाते हैं.
  • downloads: वह डायरेक्ट्री जहां आम तौर पर डाउनलोड की गई फ़ाइलें सेव की जाती हैं.
  • music: वह डायरेक्ट्री जहां आम तौर पर ऑडियो फ़ाइलें सेव की जाती हैं.
  • pictures: वह डायरेक्ट्री जहां फ़ोटो और अन्य इमेज आम तौर पर सेव की जाती हैं.
  • videos: वह डायरेक्ट्री जहां आम तौर पर वीडियो या फ़िल्में सेव की जाती हैं.

जानी-मानी सिस्टम डायरेक्ट्री के अलावा, किसी मौजूदा फ़ाइल या डायरेक्ट्री हैंडल को startIn के लिए वैल्यू के तौर पर भी पास किया जा सकता है. इसके बाद, डायलॉग बॉक्स उसी डायरेक्ट्री में खुलेगा.

// Assume `directoryHandle` is a handle to a previously opened directory.
const fileHandle = await self.showOpenFilePicker({
  startIn: directoryHandle
});

अलग-अलग फ़ाइल पिकर के मकसद के बारे में बताना

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

const fileHandle1 = await self.showSaveFilePicker({
  id: 'openText',
});

const fileHandle2 = await self.showSaveFilePicker({
  id: 'importImage',
});

IndexedDB में फ़ाइल हैंडल या डायरेक्ट्री हैंडल सेव करना

फ़ाइल हैंडल और डायरेक्ट्री हैंडल को सीरियल किया जा सकता है. इसका मतलब है कि किसी फ़ाइल या डायरेक्ट्री हैंडल को IndexedDB में सेव किया जा सकता है. इसके अलावा, एक ही टॉप-लेवल ऑरिजिन के बीच उन्हें भेजने के लिए, postMessage() को कॉल किया जा सकता है.

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

कोड के इस उदाहरण में, फ़ाइल हैंडल और डायरेक्ट्री हैंडल को सेव और वापस पाने की जानकारी दी गई है. Glitch पर इसे इस्तेमाल करते हुए देखें. (मैं कम शब्दों में बताने के लिए, idb-keyval लाइब्रेरी का इस्तेमाल करता/करती हूं.)

import { get, set } from 'https://unpkg.com/idb-keyval@5.0.2/dist/esm/index.js';

const pre1 = document.querySelector('pre.file');
const pre2 = document.querySelector('pre.directory');
const button1 = document.querySelector('button.file');
const button2 = document.querySelector('button.directory');

// File handle
button1.addEventListener('click', async () => {
  try {
    const fileHandleOrUndefined = await get('file');
    if (fileHandleOrUndefined) {
      pre1.textContent = `Retrieved file handle "${fileHandleOrUndefined.name}" from IndexedDB.`;
      return;
    }
    const [fileHandle] = await window.showOpenFilePicker();
    await set('file', fileHandle);
    pre1.textContent = `Stored file handle for "${fileHandle.name}" in IndexedDB.`;
  } catch (error) {
    alert(error.name, error.message);
  }
});

// Directory handle
button2.addEventListener('click', async () => {
  try {
    const directoryHandleOrUndefined = await get('directory');
    if (directoryHandleOrUndefined) {
      pre2.textContent = `Retrieved directroy handle "${directoryHandleOrUndefined.name}" from IndexedDB.`;
      return;
    }
    const directoryHandle = await window.showDirectoryPicker();
    await set('directory', directoryHandle);
    pre2.textContent = `Stored directory handle for "${directoryHandle.name}" in IndexedDB.`;
  } catch (error) {
    alert(error.name, error.message);
  }
});

सेव की गई फ़ाइल या डायरेक्ट्री के हैंडल और अनुमतियां

अनुमतियां हमेशा सेशन के बीच बनी नहीं रहतीं. इसलिए, आपको पुष्टि करनी चाहिए कि उपयोगकर्ता ने queryPermission() का इस्तेमाल करके, फ़ाइल या डायरेक्ट्री को अनुमति दी है या नहीं. अगर उन्होंने अनुमति नहीं दी है, तो requestPermission() को कॉल करके इसका अनुरोध करें. यह फ़ाइल और डायरेक्ट्री हैंडल के लिए भी इसी तरह काम करता है. आपको क्रमशः fileOrDirectoryHandle.requestPermission(descriptor) या fileOrDirectoryHandle.queryPermission(descriptor) चलाना होगा.

टेक्स्ट एडिटर में, मैंने verifyPermission() तरीका बनाया है. इससे यह पता चलता है कि उपयोगकर्ता ने पहले से अनुमति दी है या नहीं. ज़रूरत पड़ने पर, अनुरोध किया जाता है.

async function verifyPermission(fileHandle, readWrite) {
  const options = {};
  if (readWrite) {
    options.mode = 'readwrite';
  }
  // Check if permission was already granted. If so, return true.
  if ((await fileHandle.queryPermission(options)) === 'granted') {
    return true;
  }
  // Request permission. If the user grants permission, return true.
  if ((await fileHandle.requestPermission(options)) === 'granted') {
    return true;
  }
  // The user didn't grant permission, so return false.
  return false;
}

पढ़ने की अनुमति के साथ लिखने की अनुमति का अनुरोध करके, मैंने अनुमति के अनुरोधों की संख्या कम कर दी है; फ़ाइल खोलने पर उपयोगकर्ता को एक अनुरोध दिखता है और वह उसे पढ़ने और उसमें बदलाव करने, दोनों की अनुमति देता है.

डायरेक्ट्री खोलना और उसके कॉन्टेंट की गिनती करना

किसी डायरेक्ट्री में मौजूद सभी फ़ाइलों की सूची देखने के लिए, showDirectoryPicker() को कॉल करें. उपयोगकर्ता, पिकर में कोई डायरेक्ट्री चुनता है. इसके बाद, FileSystemDirectoryHandle दिखाया जाता है. इसकी मदद से, डायरेक्ट्री की फ़ाइलों की गिनती की जा सकती है और उन्हें ऐक्सेस किया जा सकता है. डिफ़ॉल्ट रूप से, आपके पास डायरेक्ट्री में मौजूद फ़ाइलों को पढ़ने का ऐक्सेस होगा. हालांकि, अगर आपको लिखने का ऐक्सेस चाहिए, तो { mode: 'readwrite' } को मेथड में पास करें.

butDir.addEventListener('click', async () => {
  const dirHandle = await window.showDirectoryPicker();
  for await (const entry of dirHandle.values()) {
    console.log(entry.kind, entry.name);
  }
});

उदाहरण के लिए, अगर आपको getFile() का इस्तेमाल करके हर फ़ाइल को ऐक्सेस करने की ज़रूरत है, तो हर नतीजे पर await का क्रम में इस्तेमाल न करें. इसके बजाय, सभी फ़ाइलों को एक साथ प्रोसेस करें, उदाहरण के लिए, Promise.all() का इस्तेमाल करके.

butDir.addEventListener('click', async () => {
  const dirHandle = await window.showDirectoryPicker();
  const promises = [];
  for await (const entry of dirHandle.values()) {
    if (entry.kind !== 'file') {
      continue;
    }
    promises.push(entry.getFile().then((file) => `${file.name} (${file.size})`));
  }
  console.log(await Promise.all(promises));
});

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

किसी डायरेक्ट्री से, फ़ाइलें और फ़ोल्डर बनाए या ऐक्सेस किए जा सकते हैं. इसके लिए, getFileHandle() या getDirectoryHandle() वाले तरीके का इस्तेमाल करें. create की कुंजी और true या false की बूलियन वैल्यू के साथ, विकल्प के तौर पर options ऑब्जेक्ट पास करके, यह तय किया जा सकता है कि फ़ाइल या फ़ोल्डर मौजूद न होने पर उसे बनाया जाना चाहिए या नहीं.

// In an existing directory, create a new directory named "My Documents".
const newDirectoryHandle = await existingDirectoryHandle.getDirectoryHandle('My Documents', {
  create: true,
});
// In this new directory, create a file named "My Notes.txt".
const newFileHandle = await newDirectoryHandle.getFileHandle('My Notes.txt', { create: true });

डायरेक्ट्री में किसी आइटम का पाथ हल करना

किसी डायरेक्ट्री में मौजूद फ़ाइलों या फ़ोल्डर के साथ काम करते समय, उस आइटम के पाथ का समाधान करना मददगार हो सकता है. ऐसा करने के लिए, resolve() नाम का तरीका अपनाएं. समस्या हल करने के लिए, आइटम डायरेक्ट्री का डायरेक्ट या अप्रत्यक्ष चाइल्ड हो सकता है.

// Resolve the path of the previously created file called "My Notes.txt".
const path = await newDirectoryHandle.resolve(newFileHandle);
// `path` is now ["My Documents", "My Notes.txt"]

किसी डायरेक्ट्री में मौजूद फ़ाइलें और फ़ोल्डर मिटाना

अगर आपके पास किसी डायरेक्ट्री का ऐक्सेस है, तो उसमें मौजूद फ़ाइलों और फ़ोल्डर को removeEntry() तरीके से मिटाया जा सकता है. फ़ोल्डर को मिटाने के लिए, बार-बार मिटाने का विकल्प चुना जा सकता है. साथ ही, इसमें सभी सब-फ़ोल्डर और उनमें मौजूद फ़ाइलें भी शामिल की जा सकती हैं.

// Delete a file.
await directoryHandle.removeEntry('Abandoned Projects.txt');
// Recursively delete a folder.
await directoryHandle.removeEntry('Old Stuff', { recursive: true });

किसी फ़ाइल या फ़ोल्डर को सीधे मिटाना

अगर आपके पास किसी फ़ाइल या डायरेक्ट्री हैंडल का ऐक्सेस है, तो उसे हटाने के लिए remove() को FileSystemFileHandle या FileSystemDirectoryHandle पर कॉल करें.

// Delete a file.
await fileHandle.remove();
// Delete a directory.
await directoryHandle.remove();

फ़ाइलों और फ़ोल्डर के नाम बदलना और उन्हें एक से दूसरी जगह ले जाना

FileSystemHandle इंटरफ़ेस पर move() को कॉल करके, फ़ाइलों और फ़ोल्डर का नाम बदला जा सकता है या उन्हें किसी नई जगह पर ले जाया जा सकता है. FileSystemHandle में चाइल्ड इंटरफ़ेस FileSystemFileHandle और FileSystemDirectoryHandle हैं. move() वाले तरीके में एक या दो पैरामीटर लगते हैं. पहला आर्ग्युमेंट, नए नाम वाली स्ट्रिंग या डेस्टिनेशन फ़ोल्डर के लिए FileSystemDirectoryHandle हो सकता है. बाद वाले मामले में, वैकल्पिक दूसरा पैरामीटर नए नाम वाली स्ट्रिंग है, इसलिए नाम बदलने और नाम बदलने की प्रक्रिया एक ही चरण में हो सकती है.

// Rename the file.
await file.move('new_name');
// Move the file to a new directory.
await file.move(directory);
// Move the file to a new directory and rename it.
await file.move(directory, 'newer_name');

इंटिग्रेशन को खींचें और छोड़ें

एचटीएमएल खींचें और छोड़ें इंटरफ़ेस की मदद से, वेब ऐप्लिकेशन किसी वेब पेज पर खींची गई और छोड़ी गई फ़ाइलें स्वीकार कर सकते हैं. खींचकर छोड़ने की प्रोसेस के दौरान, खींची गई फ़ाइल और डायरेक्ट्री आइटम, फ़ाइल एंट्री और डायरेक्ट्री एंट्री से जुड़े होते हैं. अगर खींचे गए आइटम की कैटगरी फ़ाइल है, तो DataTransferItem.getAsFileSystemHandle() तरीका, FileSystemFileHandle ऑब्जेक्ट के साथ एक प्रॉमिस दिखाता है. वहीं, अगर खींचे गए आइटम की कैटगरी डायरेक्ट्री है, तो यह तरीका FileSystemDirectoryHandle ऑब्जेक्ट के साथ एक प्रॉमिस दिखाता है. नीचे दी गई लिस्टिंग में, इस सुविधा के काम करने का तरीका बताया गया है. ध्यान दें कि खींचकर छोड़ने वाले इंटरफ़ेस का DataTransferItem.kind, फ़ाइलों और डायरेक्ट्री, दोनों के लिए "file" है. वहीं, File System Access API का FileSystemHandle.kind, फ़ाइलों के लिए "file" और डायरेक्ट्री के लिए "directory" है.

elem.addEventListener('dragover', (e) => {
  // Prevent navigation.
  e.preventDefault();
});

elem.addEventListener('drop', async (e) => {
  e.preventDefault();

  const fileHandlesPromises = [...e.dataTransfer.items]
    .filter((item) => item.kind === 'file')
    .map((item) => item.getAsFileSystemHandle());

  for await (const handle of fileHandlesPromises) {
    if (handle.kind === 'directory') {
      console.log(`Directory: ${handle.name}`);
    } else {
      console.log(`File: ${handle.name}`);
    }
  }
});

ओरिजनल निजी फ़ाइल सिस्टम को ऐक्सेस करना

ऑरिजिन प्राइवेट फ़ाइल सिस्टम, स्टोरेज एंडपॉइंट होता है. जैसा कि नाम से ही पता चलता है, यह पेज की शुरुआत तक निजी रहता है. आम तौर पर, ब्राउज़र इस ऑरिजिन निजी फ़ाइल सिस्टम के कॉन्टेंट को कहीं भी डिस्क पर सेव करके, इसे लागू करते हैं. हालांकि, ऐसे कॉन्टेंट को उपयोगकर्ता ऐक्सेस करने की अनुमति नहीं देते. इसी तरह, यह संभावित नहीं है कि ओरिजनल निजी फ़ाइल सिस्टम के बच्चों के नाम से मेल खाने वाले नाम वाली फ़ाइलें या डायरेक्ट्री मौजूद हों. ब्राउज़र से ऐसा लग सकता है कि अंदर कुछ फ़ाइलें हैं. हालांकि, यह एक ओरिजिनल निजी फ़ाइल सिस्टम है. इसलिए, ब्राउज़र इन "फ़ाइलों" को डेटाबेस या किसी अन्य डेटा स्ट्रक्चर में सेव कर सकता है. अगर इस एपीआई का इस्तेमाल किया जाता है, तो उम्मीद करें कि बनाई गई फ़ाइलें, हार्ड डिस्क पर एक-एक करके मिल जाएंगी. रूट FileSystemDirectoryHandle का ऐक्सेस मिलने के बाद, ऑरिजिन प्राइवेट फ़ाइल सिस्टम पर सामान्य तरीके से काम किया जा सकता है.

const root = await navigator.storage.getDirectory();
// Create a new file handle.
const fileHandle = await root.getFileHandle('Untitled.txt', { create: true });
// Create a new directory handle.
const dirHandle = await root.getDirectoryHandle('New Folder', { create: true });
// Recursively remove a directory.
await root.removeEntry('Old Stuff', { recursive: true });

ब्राउज़र के इस्तेमाल से जुड़ी सहायता

  • Chrome: 86.
  • Edge: 86.
  • Firefox: 111.
  • Safari: 15.2.

सोर्स

ऑरिजिनल निजी फ़ाइल सिस्टम से, परफ़ॉर्मेंस के लिए ऑप्टिमाइज़ की गई फ़ाइलें ऐक्सेस करना

ऑरिजिन का निजी फ़ाइल सिस्टम, एक खास तरह की फ़ाइल का ऐक्सेस देता है. यह फ़ाइल, परफ़ॉर्मेंस के लिए काफ़ी ऑप्टिमाइज़ की गई होती है. उदाहरण के लिए, फ़ाइल के कॉन्टेंट में बदलाव करने के लिए, फ़ाइल में मौजूदा जगह पर बदलाव करने का ऐक्सेस और खास ऐक्सेस दिया जाता है. Chromium 102 और उसके बाद के वर्शन में, फ़ाइल ऐक्सेस को आसान बनाने के लिए, ऑरिजिन के निजी फ़ाइल सिस्टम पर एक और तरीका दिया गया है: createSyncAccessHandle() (सिंक्रोनस पढ़ने और लिखने से जुड़ी कार्रवाइयों के लिए). यह FileSystemFileHandle पर दिखता है, लेकिन सिर्फ़ वेब वर्कर्स में.

// (Read and write operations are synchronous,
// but obtaining the handle is asynchronous.)
// Synchronous access exclusively in Worker contexts.
const accessHandle = await fileHandle.createSyncAccessHandle();
const writtenBytes = accessHandle.write(buffer);
const readBytes = accessHandle.read(buffer, { at: 1 });

पॉलीफ़िल करना

File System Access API के तरीकों को पूरी तरह से पॉलीफ़िल नहीं किया जा सकता.

  • showOpenFilePicker() तरीके का अनुमान लगाने के लिए, <input type="file"> एलिमेंट का इस्तेमाल किया जा सकता है.
  • showSaveFilePicker() तरीके को <a download="file_name"> एलिमेंट की मदद से सिम्युलेट किया जा सकता है. हालांकि, इससे प्रोग्राम के हिसाब से डाउनलोड ट्रिगर होता है और मौजूदा फ़ाइलों को ओवरराइट नहीं किया जा सकता.
  • showDirectoryPicker() वाले तरीके को कुछ हद तक, स्टैंडर्ड के मुताबिक न होने वाले <input type="file" webkitdirectory> एलिमेंट की मदद से एमुलेट किया जा सकता है.

हमने browser-fs-access नाम की एक लाइब्रेरी डेवलप की है. यह लाइब्रेरी जहां भी हो सके वहां File System Access API का इस्तेमाल करती है. साथ ही, अन्य सभी मामलों में इन सबसे अच्छे विकल्पों का इस्तेमाल करती है.

सुरक्षा और अनुमतियां

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

कोई फ़ाइल खोलना या नई फ़ाइल सेव करना

पढ़ने के लिए फ़ाइल खोलने के लिए, फ़ाइल पिकर
फ़ाइल पिकर, जिसका इस्तेमाल किसी मौजूदा फ़ाइल को पढ़ने के लिए किया जाता है.

फ़ाइल खोलने पर, उपयोगकर्ता फ़ाइल पिकर का इस्तेमाल करके, फ़ाइल या डायरेक्ट्री को पढ़ने की अनुमति देता है. ओपन फ़ाइल पिकर को सिर्फ़ उपयोगकर्ता जेस्चर का इस्तेमाल करके दिखाया जा सकता है. ऐसा सुरक्षित कॉन्टेक्स्ट से किए जाने पर ही किया जा सकता है. अगर उपयोगकर्ताओं का इरादा बदल जाता है, तो वे फ़ाइल चुनने वाले टूल में जाकर, चुने गए विकल्प को रद्द कर सकते हैं. ऐसा करने पर, साइट को किसी भी फ़ाइल का ऐक्सेस नहीं मिलता. यह वैसा ही काम करता है जैसा <input type="file"> एलिमेंट में किया जाता है.

फ़ाइल को डिस्क पर सेव करने के लिए, फ़ाइल पिकर.
फ़ाइल पिकर, जिसका इस्तेमाल डिस्क पर फ़ाइल सेव करने के लिए किया जाता है.

इसी तरह, जब कोई वेब ऐप्लिकेशन कोई नई फ़ाइल सेव करना चाहता है, तो ब्राउज़र 'फ़ाइल सेव करें' पिकर दिखाता है. इससे उपयोगकर्ता को नई फ़ाइल का नाम और जगह तय करने में मदद मिलती है. वे डिवाइस में नई फ़ाइल सेव कर रहे होते हैं, जबकि किसी मौजूदा फ़ाइल को ओवरराइट कर देते हैं. इसलिए, फ़ाइल पिकर, ऐप्लिकेशन को फ़ाइल में बदलाव करने की अनुमति देता है.

पाबंदी वाले फ़ोल्डर

उपयोगकर्ताओं और उनके डेटा को सुरक्षित रखने के लिए, हो सकता है कि उपयोगकर्ता कुछ फ़ोल्डर में फ़ाइलें सेव न कर पाए. उदाहरण के लिए, Windows जैसे कोर ऑपरेटिंग सिस्टम फ़ोल्डर या macOS Library के फ़ोल्डर. ऐसा होने पर, ब्राउज़र एक प्रॉम्प्ट दिखाता है और उपयोगकर्ता से कोई दूसरा फ़ोल्डर चुनने के लिए कहता है.

किसी मौजूदा फ़ाइल या डायरेक्ट्री में बदलाव करना

उपयोगकर्ता से साफ़ तौर पर अनुमति लिए बिना वेब ऐप्लिकेशन, डिस्क पर मौजूद किसी फ़ाइल में बदलाव नहीं कर सकता.

अनुमति का प्रॉम्प्ट

अगर कोई व्यक्ति किसी ऐसी फ़ाइल में बदलाव करना चाहता है जिसका उसने पहले पढ़ने का ऐक्सेस दिया था, तो ब्राउज़र एक अनुमति वाला प्रॉम्प्ट दिखाता है. इसमें, साइट से डिस्क पर बदलावों को सेव करने की अनुमति मांगी जाती है. अनुमति का अनुरोध सिर्फ़ उपयोगकर्ता के जेस्चर से ट्रिगर किया जा सकता है. उदाहरण के लिए, 'सेव करें' बटन पर क्लिक करके.

फ़ाइल सेव करने से पहले अनुमति का प्रॉम्प्ट दिखता है.
ब्राउज़र को किसी मौजूदा फ़ाइल में बदलाव करने की अनुमति देने से पहले, उपयोगकर्ताओं को दिखाया जाने वाला प्रॉम्प्ट.

इसके अलावा, कई फ़ाइलों में बदलाव करने वाला वेब ऐप्लिकेशन, फ़ाइल खोलने के समय बदलावों को सेव करने की अनुमति भी मांग सकता है. जैसे, आईडीई.

अगर उपयोगकर्ता 'रद्द करें' चुनता है और लिखने का ऐक्सेस नहीं देता है, तो वेब ऐप्लिकेशन, लोकल फ़ाइल में किए गए बदलावों को सेव नहीं कर सकता. इसमें उपयोगकर्ता को अपना डेटा सेव करने का कोई दूसरा तरीका उपलब्ध कराना चाहिए. उदाहरण के लिए, फ़ाइल को"डाउनलोड" करने या क्लाउड में डेटा सेव करने का तरीका.

पारदर्शिता

ऑम्निबॉक्स का आइकॉन
पता बार का आइकॉन, जो यह दिखाता है कि उपयोगकर्ता ने वेबसाइट को लोकल फ़ाइल में सेव करने की अनुमति दी है.

जब कोई उपयोगकर्ता किसी वेब ऐप्लिकेशन को लोकल फ़ाइल सेव करने की अनुमति देता है, तो ब्राउज़र पता बार में एक आइकॉन दिखाता है. आइकॉन पर क्लिक करने से एक पॉप-ओवर खुलता है. इसमें उन फ़ाइलों की सूची दिखती है जिनका ऐक्सेस उपयोगकर्ता ने दिया है. उपयोगकर्ता चाहे, तो इस ऐक्सेस को कभी भी वापस ले सकता है.

अनुमति का बना रहना

वेब ऐप्लिकेशन, फ़ाइल में किए गए बदलावों को बिना प्रॉम्प्ट के सेव करना जारी रख सकता है. ऐसा तब तक होगा, जब तक ऑरिजिन के लिए सभी टैब बंद नहीं कर दिए जाते. टैब बंद करने के बाद, साइट पर मौजूद कॉन्टेंट को ऐक्सेस नहीं किया जा सकता. अगली बार जब उपयोगकर्ता वेब ऐप्लिकेशन का इस्तेमाल करेगा, तो उसे फ़ाइलों का ऐक्सेस पाने के लिए फिर से कहा जाएगा.

सुझाव/राय दें या शिकायत करें

हम File System Access API के इस्तेमाल के आपके अनुभवों के बारे में जानना चाहते हैं.

हमें एपीआई के डिज़ाइन के बारे में बताएं

क्या एपीआई में कुछ ऐसा है जो आपकी उम्मीद के मुताबिक काम नहीं करता? या क्या कुछ ऐसे तरीके या प्रॉपर्टी हैं जिन पर आपको अपने आइडिया को लागू करने की ज़रूरत है? क्या आपका सुरक्षा मॉडल के बारे में कोई सवाल या टिप्पणी है?

क्या लागू करने में समस्या आ रही है?

क्या आपको Chrome को लागू करने में कोई गड़बड़ी मिली? या क्या इसे लागू करने का तरीका, खास जानकारी से अलग है?

  • https://new.crbug.com पर जाकर, गड़बड़ी की शिकायत करें. इसमें ज़्यादा से ज़्यादा जानकारी शामिल करें. साथ ही, गड़बड़ी को दोहराने के लिए निर्देश दें और कॉम्पोनेंट को Blink>Storage>FileSystem पर सेट करें. तुरंत जवाब देने के लिए, Glitch काम करता है.

क्या आपको एपीआई का इस्तेमाल करना है?

क्या आपको अपनी साइट पर फ़ाइल सिस्टम ऐक्सेस एपीआई का इस्तेमाल करना है? सार्वजनिक तौर पर सहायता पाने से, हमें सुविधाओं को प्राथमिकता देने में मदद मिलती है. साथ ही, इससे अन्य ब्राउज़र वेंडर को यह पता चलता है कि इन सुविधाओं को उपलब्ध कराना कितना ज़रूरी है.

  • WICG के Discourse थ्रेड पर बताएं कि आपको इसका इस्तेमाल कैसे करना है.
  • हैशटैग #FileSystemAccess का इस्तेमाल करके @ChromiumDev को ट्वीट भेजें और हमें बताएं कि उनका इस्तेमाल कहां और कैसे किया जा रहा है.

मदद के लिए लिंक

धन्यवाद

फ़ाइल सिस्टम को ऐक्सेस करने वाले एपीआई के स्पेसिफ़िकेशन को Marijn Kruisselbrink ने लिखा है.