نظام الملفات الخاصة وأصل

يقدّم معيار نظام الملفات نظام ملفات خاصًا بالمصدر (OPFS) كنقطة نهاية تخزين خاصة بمصدر الصفحة وغير مرئية للمستخدم، وتوفّر إمكانية الوصول الاختياري إلى نوع خاص من الملفات تم تحسينه بشكل كبير لتحقيق أداء أفضل.

دعم المتصفح

يتوافق نظام الملفات الخاص الأصلي مع المتصفحات الحديثة، وقد وضعت مجموعة عمل Web Hypertext Application Technology Working Group (WHATWG) معاييره في معيار نظام الملفات الحي.

Browser Support

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

Source

الحافز

عندما تفكّر في الملفات على جهاز الكمبيوتر، من المحتمل أن تفكّر في تسلسل هرمي للملفات: ملفات منظَّمة في مجلدات يمكنك استكشافها باستخدام مستكشف الملفات في نظام التشغيل. على سبيل المثال، على جهاز Windows، قد تكون قائمة المهام الخاصة بالمستخدم "أحمد" مخزّنة في C:\Users\Tom\Documents\ToDo.txt. في هذا المثال، ToDo.txt هو اسم الملف، وUsers وTom وDocuments هي أسماء المجلدات. يمثّل `C:` على نظام التشغيل Windows الدليل الجذر لمحرك الأقراص.

الطريقة النموذجية للتعامل مع الملفات على الويب

لتعديل قائمة المهام في تطبيق ويب، إليك الخطوات المعتادة:

  1. يقوم المستخدم بتحميل الملف إلى خادم أو بفتحه على الجهاز باستخدام <input type="file">.
  2. يجري المستخدم التغييرات، ثم ينزّل الملف الناتج مع <a download="ToDo.txt> الذي click() برمجيًا باستخدام JavaScript.
  3. لفتح المجلدات، عليك استخدام سمة خاصة في <input type="file" webkitdirectory>، وهي متوافقة مع جميع المتصفحات تقريبًا، على الرغم من اسمها الخاص.

طريقة حديثة للعمل مع الملفات على الويب

لا يمثّل هذا المسار الطريقة التي يفكّر بها المستخدمون في تعديل الملفات، ما يعني أنّ المستخدمين يحصلون في النهاية على نُسخ من ملفات الإدخال. لذلك، قدّمت واجهة برمجة التطبيقات File System Access API ثلاث طرق لاختيار الملفات:

يتم تفعيل مسار على النحو التالي:

  1. افتح ToDo.txt باستخدام showOpenFilePicker()، واحصل على عنصر FileSystemFileHandle.
  2. من عنصر FileSystemFileHandle، احصل على File من خلال استدعاء طريقة getFile() في معالج الملف.
  3. عدِّل الملف، ثم استدعِ requestPermission({mode: 'readwrite'}) على المعرّف.
  4. إذا وافق المستخدم على طلب الحصول على إذن، احفظ التغييرات في الملف الأصلي.
  5. يمكنك بدلاً من ذلك الاتصال بـ showSaveFilePicker() والسماح للمستخدم باختيار ملف جديد. (إذا اختار المستخدم ملفًا تم فتحه سابقًا، سيتم استبدال محتواه.) بالنسبة إلى عمليات الحفظ المتكرّرة، يمكنك الاحتفاظ بمعرّف الملف، وبالتالي لن تحتاج إلى عرض مربّع حوار حفظ الملف مرة أخرى.

قيود العمل على الملفات على الويب

تتوفّر الملفات والمجلدات التي يمكن الوصول إليها باستخدام هذه الطرق في ما يمكن تسميته نظام الملفات المرئي للمستخدم. يتم وضع علامة الويب على الملفات المحفوظة من الويب، وخاصةً الملفات التنفيذية، وبالتالي يمكن لنظام التشغيل عرض تحذير إضافي قبل تنفيذ ملف يحتمل أن يكون خطيرًا. كميزة أمان إضافية، تتم حماية الملفات التي يتم الحصول عليها من الويب أيضًا من خلال التصفّح الآمن، ويمكنك اعتبارها، من أجل التبسيط وفي سياق هذا المستند، عملية فحص للفيروسات مستندة إلى السحابة الإلكترونية. عند كتابة البيانات في ملف باستخدام File System Access API، لا تتم الكتابة في مكانها، بل يتم استخدام ملف مؤقت. ولا يتم تعديل الملف نفسه إلا إذا اجتاز جميع عمليات التحقّق من الأمان هذه.

ويؤدي ذلك إلى بطء عمليات الملفات نسبيًا، على الرغم من التحسينات التي تم تطبيقها حيثما أمكن، مثل على أجهزة macOS. ومع ذلك، فإنّ كل استدعاء write() يحتوي على جميع المعلومات اللازمة، لذا فهو يفتح الملف ويحدد الإزاحة المحددة ثم يكتب البيانات.

الملفات كأساس للمعالجة

في الوقت نفسه، تُعد الملفات طريقة ممتازة لتسجيل البيانات. على سبيل المثال، تخزّن SQLite قواعد البيانات بأكملها في ملف واحد. ومن الأمثلة الأخرى على ذلك خرائط MIP المستخدَمة في معالجة الصور. خرائط Mipmap هي تسلسلات محسّنة ومحسوبة مسبقًا من الصور، كل منها عبارة عن تمثيل بدرجة دقة أقل تدريجيًا من الصورة السابقة، ما يجعل العديد من العمليات، مثل التكبير، أسرع. إذًا، كيف يمكن لتطبيقات الويب الاستفادة من مزايا الملفات بدون تكبّد تكاليف الأداء المرتبطة بمعالجة الملفات المستندة إلى الويب؟ الإجابة هي نظام الملفات الخاص بالمنشأ.

نظام الملفات الخاص بالجهة المالكة مقابل نظام الملفات المرئي للمستخدم

على عكس نظام الملفات المرئي للمستخدم والذي يتم تصفّحه باستخدام مستكشف الملفات في نظام التشغيل، حيث يمكنك قراءة الملفات والمجلدات وكتابتها ونقلها وإعادة تسميتها، فإنّ نظام الملفات الخاص بالمصدر ليس مخصّصًا ليطّلع عليه المستخدمون. الملفات والمجلدات في نظام الملفات الخاص بالمصدر، كما يشير الاسم، تكون خاصة، وبشكل أكثر تحديدًا، تكون خاصة بالمصدر لأحد المواقع الإلكترونية. يمكنك معرفة مصدر الصفحة من خلال كتابة location.origin في &quot;وحدة التحكّم&quot; ضمن &quot;أدوات مطوّري البرامج&quot;. على سبيل المثال، مصدر الصفحة https://developer.chrome.com/articles/ هو https://developer.chrome.com. يمكنك الاطّلاع على مزيد من المعلومات حول نظرية المصادر في مقالة التعرّف على "الموقع الإلكتروني نفسه" و "المصدر نفسه".

يمكن لجميع الصفحات التي تشترك في المصدر نفسه الاطّلاع على بيانات نظام الملفات الخاص بالمصدر نفسه، وبالتالي يمكن لـ https://developer.chrome.com/docs/extensions/mv3/getstarted/extensions-101/ الاطّلاع على التفاصيل نفسها الواردة في المثال السابق. لكل مصدر نظام ملفات خاص به ومستقل، ما يعني أنّ نظام ملفات المصدر الخاص بالمصدر https://developer.chrome.com يختلف تمامًا عن نظام ملفات المصدر الخاص بالمصدر https://web.dev مثلاً. في نظام التشغيل Windows، يكون دليل الجذر لنظام الملفات المرئي للمستخدم هو C:\\.

والبديل لنظام الملفات الخاص بالمصدر هو دليل جذر فارغ في البداية لكل مصدر يتم الوصول إليه من خلال استدعاء الطريقة غير المتزامنة navigator.storage.getDirectory().

للاطّلاع على مقارنة بين نظام الملفات المرئي للمستخدم ونظام الملفات الخاص بالمصدر، راجِع المخطط التالي. يوضّح المخطط أنّ كل شيء، باستثناء الدليل الجذر، هو نفسه من الناحية النظرية، مع تسلسل هرمي للملفات والمجلدات لتنظيمها وترتيبها حسب الحاجة لتلبية احتياجاتك من البيانات والتخزين.

مخطّط لنظام الملفات المرئي للمستخدم ونظام الملفات الخاص بالمصدر مع تسلسلين هرميين نموذجيين للملفات
نقطة الدخول إلى نظام الملفات المرئي للمستخدم هي قرص ثابت رمزي، ونقطة الدخول إلى نظام الملفات الخاص بالمصدر هي استدعاء الطريقة navigator.storage.getDirectory.

تفاصيل نظام الملفات الخاص بالمصدر

وكما هو الحال مع آليات التخزين الأخرى في المتصفّح (مثل localStorage أو IndexedDB)، يخضع نظام الملفات الخاص بالمصدر لقيود الحصة في المتصفّح. عندما يمحو المستخدم جميع بيانات التصفّح أو جميع بيانات الموقع الإلكتروني، سيتم أيضًا حذف نظام الملفات الخاص بالمصدر.

اتّصِل بـ navigator.storage.estimate() وفي عنصر الرد الناتج، اطّلِع على الإدخال usage لمعرفة مقدار مساحة التخزين التي يستهلكها تطبيقك حاليًا، والتي يتم تقسيمها حسب آلية التخزين في العنصر usageDetails الذي تريد البحث فيه عن الإدخال fileSystem تحديدًا. بما أنّ نظام الملفات الخاص بالمصدر غير مرئي للمستخدم، لن تظهر أي طلبات للحصول على أذونات ولن يتم إجراء أي عمليات فحص من خلال ميزة "التصفّح الآمن".

الحصول على إذن الوصول إلى الدليل الجذر

للوصول إلى الدليل الجذر، نفِّذ الأمر التالي. سينتهي بك الأمر بالحصول على معرّف دليل فارغ، وتحديدًا FileSystemDirectoryHandle.

const opfsRoot = await navigator.storage.getDirectory();
// A FileSystemDirectoryHandle whose type is "directory"
// and whose name is "".
console.log(opfsRoot);

سلسلة التعليمات الرئيسية أو Web Worker

يمكن استخدام نظام الملفات الخاص بالمصدر بطريقتَين: على سلسلة التعليمات الرئيسية أو في Web Worker. لا يمكن لبرامج Web Workers حظر سلسلة التعليمات الرئيسية، ما يعني أنّه يمكن أن تكون واجهات برمجة التطبيقات متزامنة في هذا السياق، وهو نمط غير مسموح به بشكل عام في سلسلة التعليمات الرئيسية. يمكن أن تكون واجهات برمجة التطبيقات المتزامنة أسرع لأنّها لا تتطلّب التعامل مع الوعود، وعمليات الملفات تكون عادةً متزامنة في لغات مثل C التي يمكن تجميعها إلى WebAssembly.

// This is synchronous C code.
FILE *f;
f = fopen("example.txt", "w+");
fputs("Some text\n", f);
fclose(f);

إذا كنت بحاجة إلى أسرع عمليات ممكنة للملفات، أو إذا كنت تتعامل مع WebAssembly، انتقِل إلى استخدام نظام الملفات الخاص بالمصدر في Web Worker.

استخدام نظام الملفات الخاص بالمصدر على سلسلة التعليمات الرئيسية

إنشاء ملفات ومجلدات جديدة

بعد إنشاء مجلد جذر، يمكنك إنشاء ملفات ومجلدات باستخدام الطريقتَين getFileHandle() وgetDirectoryHandle() على التوالي. من خلال تمرير {create: true}، سيتم إنشاء الملف أو المجلد إذا لم يكن متوفّرًا. يمكنك إنشاء تسلسل هرمي للملفات من خلال استدعاء هذه الدوال باستخدام دليل تم إنشاؤه حديثًا كنقطة بداية.

const fileHandle = await opfsRoot
    .getFileHandle('my first file', {create: true});
const directoryHandle = await opfsRoot
    .getDirectoryHandle('my first folder', {create: true});
const nestedFileHandle = await directoryHandle
    .getFileHandle('my first nested file', {create: true});
const nestedDirectoryHandle = await directoryHandle
    .getDirectoryHandle('my first nested folder', {create: true});

تسلسل الملفات الناتج من عينة التعليمات البرمجية السابقة

الوصول إلى الملفات والمجلدات الحالية

إذا كنت تعرف اسم الملف أو المجلد، يمكنك الوصول إلى الملفات والمجلدات التي تم إنشاؤها سابقًا من خلال استدعاء الطريقتَين getFileHandle() أو getDirectoryHandle()، مع إدخال اسم الملف أو المجلد.

const existingFileHandle = await opfsRoot.getFileHandle('my first file');
const existingDirectoryHandle = await opfsRoot
    .getDirectoryHandle('my first folder');

الحصول على الملف المرتبط بمقبض ملف للقراءة

يمثّل FileSystemFileHandle ملفًا في نظام الملفات. للحصول على File المرتبط، استخدِم طريقة getFile(). File هو نوع محدّد من Blob، ويمكن استخدامه في أي سياق يمكن فيه استخدام Blob.

على وجه الخصوص، تقبل FileReader وURL.createObjectURL() وcreateImageBitmap() وXMLHttpRequest.send() كلاً من Blobs وFiles. يؤدي الحصول على File من FileSystemFileHandle إلى "تحرير" البيانات، ما يتيح لك الوصول إليها وإتاحتها لنظام الملفات المرئي للمستخدم.

const file = await fileHandle.getFile();
console.log(await file.text());

الكتابة في ملف من خلال البث

يمكنك بث البيانات إلى ملف من خلال استدعاء createWritable() الذي ينشئ FileSystemWritableFileStream يمكنك بعد ذلك write() المحتوى إليه. في النهاية، عليك close() البث.

const contents = 'Some text';
// Get a writable stream.
const writable = await fileHandle.createWritable();
// Write the contents of the file to the stream.
await writable.write(contents);
// Close the stream, which persists the contents.
await writable.close();

حذف الملفات والمجلدات

احذف الملفات والمجلدات من خلال استدعاء طريقة remove() معيّنة لمقبض الملف أو الدليل. لحذف مجلد يتضمّن جميع المجلدات الفرعية، استخدِم الخيار {recursive: true}.

await fileHandle.remove();
await directoryHandle.remove({recursive: true});

كبديل، إذا كنت تعرف اسم الملف أو المجلد المطلوب حذفه في دليل، استخدِم طريقة removeEntry().

directoryHandle.removeEntry('my first nested file');

نقل الملفات والمجلدات وإعادة تسميتها

إعادة تسمية الملفات والمجلدات ونقلها باستخدام الطريقة move() يمكن أن تتم عملية النقل وإعادة التسمية معًا أو بشكل منفصل.

// Rename a file.
await fileHandle.move('my first renamed file');
// Move a file to another directory.
await fileHandle.move(nestedDirectoryHandle);
// Move a file to another directory and rename it.
await fileHandle
    .move(nestedDirectoryHandle, 'my first renamed and now nested file');

حلّ مسار ملف أو مجلد

لمعرفة مكان ملف أو مجلد معيّن بالنسبة إلى دليل مرجعي، استخدِم طريقة resolve()، مع تمرير FileSystemHandle كمعلَمة. للحصول على المسار الكامل لملف أو مجلد في نظام الملفات الخاص بالمصدر، استخدِم الدليل الجذر كدليل مرجعي تم الحصول عليه باستخدام navigator.storage.getDirectory().

const relativePath = await opfsRoot.resolve(nestedDirectoryHandle);
// `relativePath` is `['my first folder', 'my first nested folder']`.

التحقّق مما إذا كان مؤشرا ملف أو مجلد يشيران إلى الملف أو المجلد نفسه

في بعض الأحيان، يكون لديك معرّفان ولا تعرف ما إذا كانا يشيران إلى الملف أو المجلد نفسه. للتحقّق من ذلك، استخدِم طريقة isSameEntry().

fileHandle.isSameEntry(nestedFileHandle);
// Returns `false`.

عرض محتوى مجلد

FileSystemDirectoryHandle هو مكرّر غير متزامن يمكنك تكراره باستخدام حلقة for await... of. وبما أنّها مكرّر غير متزامن، فهي تتيح أيضًا استخدام الطرق entries() وvalues() وkeys()، التي يمكنك الاختيار من بينها حسب المعلومات التي تحتاج إليها:

for await (let [name, handle] of directoryHandle) {}
for await (let [name, handle] of directoryHandle.entries()) {}
for await (let handle of directoryHandle.values()) {}
for await (let name of directoryHandle.keys()) {}

عرض محتوى مجلد وجميع المجلدات الفرعية بشكل متكرر

من السهل الوقوع في الخطأ عند التعامل مع الحلقات والدوال غير المتزامنة المقترنة بالتكرار العودي. يمكن أن تكون الدالة التالية نقطة بداية لإدراج محتويات مجلد وجميع مجلداته الفرعية، بما في ذلك جميع الملفات وأحجامها. يمكنك تبسيط الدالة إذا لم تكن بحاجة إلى أحجام الملفات، وذلك من خلال عدم إرسال الوعد handle.getFile() في الموضع directoryEntryPromises.push، بل إرسال handle مباشرةً.

const getDirectoryEntriesRecursive = async (
  directoryHandle,
  relativePath = '.',
) => {
  const fileHandles = [];
  const directoryHandles = [];
  const entries = {};
  // Get an iterator of the files and folders in the directory.
  const directoryIterator = directoryHandle.values();
  const directoryEntryPromises = [];
  for await (const handle of directoryIterator) {
    const nestedPath = `${relativePath}/${handle.name}`;
    if (handle.kind === 'file') {
      fileHandles.push({ handle, nestedPath });
      directoryEntryPromises.push(
        handle.getFile().then((file) => {
          return {
            name: handle.name,
            kind: handle.kind,
            size: file.size,
            type: file.type,
            lastModified: file.lastModified,
            relativePath: nestedPath,
            handle
          };
        }),
      );
    } else if (handle.kind === 'directory') {
      directoryHandles.push({ handle, nestedPath });
      directoryEntryPromises.push(
        (async () => {
          return {
            name: handle.name,
            kind: handle.kind,
            relativePath: nestedPath,
            entries:
                await getDirectoryEntriesRecursive(handle, nestedPath),
            handle,
          };
        })(),
      );
    }
  }
  const directoryEntries = await Promise.all(directoryEntryPromises);
  directoryEntries.forEach((directoryEntry) => {
    entries[directoryEntry.name] = directoryEntry;
  });
  return entries;
};

استخدام نظام الملفات الخاص بالمصدر في Web Worker

كما ذكرنا سابقًا، لا يمكن لبرامج Web Workers حظر سلسلة التعليمات الرئيسية، ولهذا السبب يُسمح في هذا السياق باستخدام الطرق المتزامنة.

الحصول على أداة معالجة الوصول المتزامن

نقطة الدخول إلى أسرع عمليات الملفات الممكنة هي FileSystemSyncAccessHandle، التي يتم الحصول عليها من FileSystemFileHandle عادي من خلال استدعاء createSyncAccessHandle().

const fileHandle = await opfsRoot
    .getFileHandle('my highspeed file.txt', {create: true});
const syncAccessHandle = await fileHandle.createSyncAccessHandle();

طُرق الملفات المتزامنة في مكانها

بعد الحصول على أداة معالجة للوصول المتزامن، يمكنك الوصول إلى طرق سريعة للملفات في مكانها، وكلها متزامنة.

  • getSize(): تعرض حجم الملف بالبايت.
  • write(): تكتب هذه السمة محتوى المخزن المؤقت في الملف، مع إمكانية تحديد إزاحة معينة، وتعرض عدد البايتات المكتوبة. يسمح التحقّق من عدد وحدات البايت المكتوبة التي تم عرضها للمتصلين برصد الأخطاء والكتابات الجزئية والتعامل معها.
  • read(): يقرأ هذا الإجراء محتوى الملف في مخزن مؤقت، ويمكنه اختياريًا إجراء ذلك عند إزاحة معيّنة.
  • truncate(): تغيّر حجم الملف إلى الحجم المحدّد.
  • flush(): يضمن هذا الإجراء أنّ محتوى الملف يتضمّن جميع التعديلات التي تم إجراؤها من خلال write().
  • close(): تُستخدَم لإغلاق رمز الوصول.

في ما يلي مثال يستخدم كل طريقة.

const opfsRoot = await navigator.storage.getDirectory();
const fileHandle = await opfsRoot.getFileHandle('fast', {create: true});
const accessHandle = await fileHandle.createSyncAccessHandle();

const textEncoder = new TextEncoder();
const textDecoder = new TextDecoder();

// Initialize this variable for the size of the file.
let size;
// The current size of the file, initially `0`.
size = accessHandle.getSize();
// Encode content to write to the file.
const content = textEncoder.encode('Some text');
// Write the content at the beginning of the file.
accessHandle.write(content, {at: size});
// Flush the changes.
accessHandle.flush();
// The current size of the file, now `9` (the length of "Some text").
size = accessHandle.getSize();

// Encode more content to write to the file.
const moreContent = textEncoder.encode('More content');
// Write the content at the end of the file.
accessHandle.write(moreContent, {at: size});
// Flush the changes.
accessHandle.flush();
// The current size of the file, now `21` (the length of
// "Some textMore content").
size = accessHandle.getSize();

// Prepare a data view of the length of the file.
const dataView = new DataView(new ArrayBuffer(size));

// Read the entire file into the data view.
accessHandle.read(dataView);
// Logs `"Some textMore content"`.
console.log(textDecoder.decode(dataView));

// Read starting at offset 9 into the data view.
accessHandle.read(dataView, {at: 9});
// Logs `"More content"`.
console.log(textDecoder.decode(dataView));

// Truncate the file after 4 bytes.
accessHandle.truncate(4);

نسخ ملف من نظام الملفات الخاص بالمصدر إلى نظام الملفات المرئي للمستخدم

كما ذكرنا أعلاه، لا يمكن نقل الملفات من نظام الملفات الخاص الأصلي إلى نظام الملفات المرئي للمستخدم، ولكن يمكنك نسخ الملفات. بما أنّ showSaveFilePicker() لا يتم عرضه إلا في سلسلة التعليمات الرئيسية، وليس في سلسلة تعليمات Worker، احرص على تشغيل الرمز هناك.

// On the main thread, not in the Worker. This assumes
// `fileHandle` is the `FileSystemFileHandle` you obtained
// the `FileSystemSyncAccessHandle` from in the Worker
// thread. Be sure to close the file in the Worker thread first.
const fileHandle = await opfsRoot.getFileHandle('fast');
try {
  // Obtain a file handle to a new file in the user-visible file system
  // with the same name as the file in the origin private file system.
  const saveHandle = await showSaveFilePicker({
    suggestedName: fileHandle.name || ''
  });
  const writable = await saveHandle.createWritable();
  await writable.write(await fileHandle.getFile());
  await writable.close();
} catch (err) {
  console.error(err.name, err.message);
}

تصحيح أخطاء نظام الملفات الخاص بالمصدر

إلى أن تتم إضافة ميزة مدمجة في "أدوات مطوّري البرامج" (راجِع crbug/1284595)، يمكنك استخدام إضافة Chrome OPFS Explorer لتصحيح أخطاء نظام الملفات الخاص بالمصدر. تم التقاط لقطة الشاشة من القسم إنشاء ملفات ومجلدات جديدة مباشرةً من الإضافة.

إضافة OPFS Explorer أدوات مطوري البرامج في Chrome في &quot;سوق Chrome الإلكتروني&quot;

بعد تثبيت الإضافة، افتح &quot;أدوات مطوّري البرامج في Chrome&quot;، واختَر علامة التبويب OPFS Explorer، وستكون جاهزًا لفحص التسلسل الهرمي للملفات. يمكنك حفظ الملفات من نظام الملفات الخاص بالمصدر إلى نظام الملفات المرئي للمستخدم من خلال النقر على اسم الملف، وحذف الملفات والمجلدات من خلال النقر على رمز سلة المهملات.

عرض توضيحي

يمكنك الاطّلاع على نظام الملفات الخاص بالمصدر أثناء العمل (في حال تثبيت إضافة OPFS Explorer) في عرض توضيحي يستخدمه كخادم خلفي لقاعدة بيانات SQLite تم تجميعها إلى WebAssembly. يُرجى الاطّلاع على الرمز المصدر على GitHub. لاحظ أنّ الإصدار المضمّن لا يستخدم الخلفية الخاصة بنظام الملفات الخاص بالمصدر (لأنّ إطار iframe هو متعدّد المصادر)، ولكن عند فتح العرض التوضيحي في علامة تبويب منفصلة، يتم استخدامها.

الخاتمة

لقد شكّل نظام الملفات الخاص بالجهة الأصلية، كما هو محدّد من قِبل WHATWG، الطريقة التي نستخدم بها الملفات ونتفاعل معها على الويب. وقد أتاحت حالات استخدام جديدة كان من المستحيل تحقيقها باستخدام نظام الملفات المرئي للمستخدم. تتشارك جميع الشركات الرئيسية المصنّعة للمتصفّحات، أي Apple وMozilla وGoogle، رؤية مشتركة وتعمل على تحقيقها. إنّ تطوير نظام الملفات الخاص بالمصدر هو جهد تعاوني، وملاحظات المطوّرين والمستخدمين ضرورية لإحراز تقدّم فيه.

بما أنّنا نواصل تحسين المعيار، نرحّب بتلقّي ملاحظات حول مستودع whatwg/fs في شكل مشاكل أو طلبات سحب.

الإقرارات

تمت مراجعة هذا المستند من قِبل أوستن سولي وإتيان نويل وراشيل أندرو.