واجهة برمجة التطبيقات File System Access API: تبسيط الوصول إلى الملفات على الجهاز

تسمح واجهة File System Access API لتطبيقات الويب بقراءة التغييرات أو حفظها مباشرةً في الملفات والمجلدات على جهاز المستخدم.

ما هي واجهة برمجة التطبيقات File System Access API؟

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

إذا كنت قد عملت في قراءة الملفات وكتابتها من قبل، فإن الكثير مما أنا على وشك مشاركته سيكون المألوفة لك. وأنصحك بقراءته بأية حال، لأن بعض الأنظمة ليست متشابهة.

تتوافق واجهة برمجة التطبيقات File System Access API مع معظم متصفّحات Chromium على أنظمة التشغيل Windows وmacOS وChromeOS وLinux وهناك استثناء بارز وهو Brave حيث متوفّرة حاليًا فقط عند الإبلاغ عن مشكلة. يجري العمل على دعم Android في سياق crbug.com/1011535.

استخدام واجهة برمجة التطبيقات File System Access API

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

دعم المتصفح

دعم المتصفح

  • الإصدار 86 من متصفّح Chrome
  • الحافة: 86.
  • Firefox: غير مدعوم.
  • Safari: غير متاح.

المصدر

رصد الميزات

لمعرفة ما إذا كانت واجهة برمجة التطبيقات File System Access API متوافقة، تحقّق من طريقة الاختيار التي تهتم بها.

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. الذي يحتوي على كائن ثنائي كبير. للحصول على البيانات من الكائن الثنائي الكبير (blob)، اطلب أحد العناصر الطرق، (slice()، stream(), text()، أو arrayBuffer()).

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

يمكن فقط قراءة الكائن File الذي يعرضه FileSystemFileHandle.getFile() ما دامت لم يتغير الملف الأساسي على القرص. إذا تم تعديل الملف على القرص، يصبح الكائن File غير قابل للقراءة وعليك الاتصال بـ getFile() مرة أخرى للحصول على كائن File جديد لقراءة ما تم تغييره البيانات.

خلاصة ما سبق ذكره

عندما ينقر المستخدمون على الزر فتح، يعرض المتصفّح أداة اختيار الملفات. بمجرد تحديده، لا يتم يقرأ التطبيق المحتوى ويضعه في <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 Text.txt بدلاً من Untitled. إِنْتَ تحقيق ذلك من خلال تمرير السمة suggestedName كجزء من خيارات showSaveFilePicker.

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

ينطبق الشيء نفسه على دليل البدء الافتراضي. إذا كنت تنشئ محرر نصوص، قد تحتاج إلى بدء حفظ الملف أو فتح مربع حوار فتح الملف في مجلد documents التلقائي، في حين أنه إذا كانت صورة المحرر، قد يريد البدء في مجلد pictures التلقائي. يمكنك اقتراح بداية تلقائية بتمرير السمة startIn إلى showSaveFilePicker أو showDirectoryPicker() أو showOpenFilePicker طرق من هذا القبيل.

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

مؤشرات الملفات وأسماء الدليل قابلة للتسلسل، مما يعني أنه يمكنك حفظ ملف أو مؤشر الدليل إلى قاعدة البيانات المفهرسة، أو يمكنك طلب 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() على التوالي . من خلال إدخال كائن options اختياري مع مفتاح create وقيمة منطقية هي true أو false، يمكنك تحديد ما إذا كان يجب إنشاء ملف أو مجلد جديد إذا لم يكن متوفّرًا.

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

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

يمكن إعادة تسمية الملفات والمجلدات أو نقلها إلى مكان جديد من خلال الاتصال بالرقم move() على الواجهة FileSystemHandle. 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');

دمج السحب والإفلات

تشير رسالة الأشكال البيانية واجهات السحب والإفلات بتنسيق HTML تمكين تطبيقات الويب من قبول الملفات التي تم سحبها وإفلاتها على صفحة ويب. أثناء عملية السحب والإفلات، يتم ربط عناصر الملف الذي يتم سحبه والدليل مع إدخالات الملف وإدخالات الدليل على التوالي. DataTransferItem.getAsFileSystemHandle() ترجع وعدًا بكائن FileSystemFileHandle إذا كان العنصر الذي تم سحبه ملفًا تعدك باستخدام الكائن FileSystemDirectoryHandle إذا كان العنصر الذي تم سحبه دليلاً. بطاقة بيانات المتجر التالية يوضح ذلك عمليًا. لاحظ أن واجهة السحب والإفلات DataTransferItem.kind هو "file" لكل من الملفات و الأدلة، في حين أن FileSystemHandle.kind من File System Access API هو "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 });

دعم المتصفح

  • الإصدار 86 من متصفّح Chrome
  • الحافة: 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 تستخدم الملف واجهة برمجة تطبيقات System Access API حيثما أمكن، وتعود إلى أفضل الخيارات التالية في جميع الحالات.

الأمان والأذونات

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

فتح ملف أو حفظ ملف جديد

أداة اختيار الملفات لفتح ملف وقراءته
أداة اختيار ملفات تُستخدم لفتح ملف حالي للقراءة.

عند فتح ملف، يوفر المستخدم إذنًا لقراءة ملف أو دليل باستخدام منتقي الملفات. لا يمكن عرض أداة اختيار الملفات المفتوحة إلا باستخدام إيماءة المستخدم عند عرضها من آمن السياقية. إذا غيّر المستخدمون رأيهم، يمكنهم إلغاء الاختيار في الملف. ولا يتمكن الموقع من الوصول إلى أي شيء. وهذا هو نفس سلوك دالة العنصر <input type="file">.

منتقي الملفات لحفظ ملف على القرص.
منتقي الملفات المستخدم لحفظ ملف على القرص.

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

المجلدات المحظورة

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

تعديل ملف أو دليل حالي

لا يمكن لتطبيق الويب تعديل ملف على القرص بدون الحصول على إذن صريح من المستخدم.

طلب الإذن

إذا أراد شخص حفظ تغييرات على ملف سبق له منحه إذن الوصول للقراءة، سيتعين على المتصفح تعرض رسالة طلب للحصول على إذن، تطلب الإذن للموقع الإلكتروني لكتابة تغييرات على القرص. لا يمكن بدء طلب الإذن إلا من خلال إيماءة المستخدم، على سبيل المثال، من خلال النقر على "حفظ" .

يتم عرض طلب الإذن قبل حفظ ملف.
طلب يظهر للمستخدمين قبل منح المتصفِّح إمكانية الكتابة إذن على ملف حالي.

يمكن أيضًا لتطبيق الويب الذي يعدِّل ملفات متعددة، مثل بيئة تطوير متكاملة (IDE)، طلب الإذن لحفظ الملفات. التغييرات في وقت الفتح.

إذا اختار المستخدم "إلغاء"، ولم يمنح إذن الوصول للكتابة، فلن يتمكن تطبيق الويب من حفظ التغييرات في ملف محلي. يجب أن يوفر طريقة بديلة للمستخدم لحفظ بياناته، مثلًا من خلال توفير طريقة "لتنزيل" الملف أو حفظ البيانات في السحابة.

الشفافية

رمز المربّع المتعدد الاستخدامات
رمز شريط العناوين يشير إلى أن المستخدم منح الموقع الإلكتروني الإذن حفظ في ملف محلي.

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

استمرار الإذن

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

ملاحظات

نودّ معرفة رأيك بشأن تجاربك في استخدام واجهة File System Access API.

أخبِرنا عن تصميم واجهة برمجة التطبيقات

هل هناك أي مشكلة في واجهة برمجة التطبيقات لا تعمل كما توقعت؟ أم أن هناك طرق مفقودة أو الخصائص التي تحتاج إليها لتنفيذ فكرتك؟ طرح سؤال أو تعليق بشأن الأمان النموذج؟

هل تواجه مشكلة في عملية التنفيذ؟

هل واجهت مشكلة في التنفيذ في Chrome؟ أم أن التنفيذ يختلف عن المواصفات؟

  • يمكنك الإبلاغ عن الخطأ على https://new.crbug.com. تأكد من تضمين أكبر قدر ممكن من التفاصيل، إرشادات لإعادة إنتاجه، وتعيين المكونات على Blink>Storage>FileSystem. يعمل الخطأ بشكلٍ رائع لمشاركة عمليات إعادة الإنتاج السريعة.

هل تخطط لاستخدام واجهة برمجة التطبيقات؟

هل تخطّط لاستخدام File System Access API على موقعك الإلكتروني؟ يساعدنا دعمك العام في تحديد الأولويات وتوضح لموردي المتصفح الآخرين مدى أهمية دعمهم لها.

روابط مفيدة

شكر وتقدير

تمت كتابة مواصفات واجهة برمجة التطبيقات File System Access API بواسطة ماريين كرويسلبرينك