تسمح واجهة برمجة التطبيقات File System Access API لتطبيقات الويب بقراءة التغييرات أو حفظها مباشرةً في الملفات والمجلدات على جهاز المستخدم.
تاريخ النشر: 19 أغسطس 2024
تتيح واجهة برمجة التطبيقات File System Access API للمطوّرين إنشاء تطبيقات ويب فعّالة تتفاعل مع الملفات على جهاز المستخدم المحلي، مثل بيئات التطوير المتكاملة وأدوات تعديل الصور والفيديوهات وأدوات تحرير النصوص وغيرها. بعد أن يمنح المستخدم تطبيق ويب إذن الوصول، تتيح له واجهة برمجة التطبيقات هذه قراءة التغييرات أو حفظها مباشرةً في الملفات والمجلدات على جهاز المستخدم. بالإضافة إلى قراءة الملفات وكتابتها، تتيح واجهة File System Access API إمكانية فتح دليل وإدراج محتواه.
إذا سبق لك العمل على قراءة الملفات وكتابتها، سيكون معظم ما سأشاركه معك مألوفًا لك. أنصحك بقراءتها على أي حال، لأنّ الأنظمة تختلف عن بعضها.
تتوافق واجهة برمجة التطبيقات File System Access API مع معظم متصفّحات Chromium على أنظمة التشغيل Windows وmacOS وChromeOS وLinux وAndroid. ويُستثنى من ذلك متصفّح Brave الذي لا يتوفّر فيه حاليًا إلا من خلال علامة.
استخدام File System Access API
لعرض إمكانات واجهة برمجة التطبيقات File System Access API ومدى فائدتها، كتبت محرّر نصوص في ملف واحد. يتيح لك فتح ملف نصي وتعديله وحفظ التغييرات على القرص أو بدء ملف جديد وحفظ التغييرات على القرص. لا يتضمّن هذا المستند أي تفاصيل معقّدة، ولكنّه يقدّم معلومات كافية لمساعدتك في فهم المفاهيم.
دعم المتصفح
رصد الميزات
لمعرفة ما إذا كانت واجهة برمجة التطبيقات 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
يحتوي على كائن ثنائي كبير الحجم. للحصول على البيانات من الكائن الثنائي الكبير، عليك استدعاء إحدى طرق الكائن الثنائي الكبير (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;
});
كتابة الملف في نظام الملفات على الجهاز
في محرر النصوص، تتوفّر طريقتان لحفظ ملف: حفظ وحفظ باسم. تؤدي الدالة Save إلى إعادة كتابة التغييرات في الملف الأصلي باستخدام معرّف الملف الذي تم استرداده سابقًا. لكنّ الخيار حفظ باسم ينشئ ملفًا جديدًا، وبالتالي يتطلّب معرّف ملف جديدًا.
إنشاء ملف جديد
لحفظ ملف، استدعِ الدالة 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
يمكن تسلسل معرّفات الملفات ومعرّفات الدليل، ما يعني أنّه يمكنك حفظ معرّف ملف أو دليل في 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 });
الوصول إلى الملفات المحسّنة لتحقيق أداء أفضل من نظام الملفات الخاص بالمصدر
يوفر نظام الملفات الخاص بالمصدر إمكانية الوصول الاختياري إلى نوع خاص من الملفات تم تحسينه بشكل كبير من أجل الأداء، وذلك مثلاً من خلال توفير إمكانية الكتابة الحصرية والمباشرة في محتوى الملف. في الإصدار 102 من Chromium والإصدارات الأحدث، تتوفّر طريقة إضافية في نظام الملفات الخاص بالمصدر لتسهيل الوصول إلى الملفات: createSyncAccessHandle() (لعمليات القراءة والكتابة المتزامنة).
يتم عرضه على FileSystemFileHandle، ولكن حصريًا في
Web Workers.
// (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 });
Polyfilling
لا يمكن توفير وظائف بديلة كاملة لطُرق 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 واجهة برمجة التطبيقات File System Access API ونفّذها باستخدام المبادئ الأساسية المحدّدة في التحكّم في الوصول إلى الميزات الفعّالة لمنصة الويب، بما في ذلك تحكّم المستخدم وشفافيته، بالإضافة إلى سهولة استخدامها.
فتح ملف أو حفظ ملف جديد
عند فتح ملف، يمنح المستخدم الإذن بقراءة ملف أو دليل باستخدام أداة اختيار الملفات.
لا يمكن عرض أداة اختيار الملفات المفتوحة إلا باستخدام إيماءة من المستخدم عند عرضها من سياق آمن. إذا غيّر المستخدمون رأيهم، يمكنهم إلغاء الاختيار في أداة اختيار الملفات، ولن يتمكّن الموقع الإلكتروني من الوصول إلى أي شيء. وهذا السلوك مماثل لسلوك العنصر
<input type="file">.
وبالمثل، عندما يريد تطبيق ويب حفظ ملف جديد، يعرض المتصفّح أداة اختيار حفظ الملف، ما يتيح للمستخدم تحديد اسم الملف الجديد وموقعه. بما أنّ المستخدم يحفظ ملفًا جديدًا على الجهاز (بدلاً من استبدال ملف حالي)، تمنح أداة اختيار الملفات التطبيق إذنًا بالكتابة إلى الملف.
المجلدات المحظورة
للمساعدة في حماية المستخدمين وبياناتهم، قد يحدّ المتصفّح من قدرة المستخدم على الحفظ في مجلدات معيّنة، مثل مجلدات نظام التشغيل الأساسي، مثل Windows ومجلدات مكتبة macOS. عند حدوث ذلك، يعرض المتصفّح رسالة ويطلب من المستخدم اختيار مجلد مختلف.
تعديل ملف أو دليل حالي
لا يمكن لتطبيق ويب تعديل ملف على القرص بدون الحصول على إذن صريح من المستخدم.
طلب الإذن
إذا أراد مستخدم حفظ التغييرات في ملف سبق أن منح إذن الوصول للقراءة إليه، يعرض المتصفّح طلب إذن، ويطلب من الموقع الإلكتروني الإذن بكتابة التغييرات على القرص. لا يمكن أن يتم تشغيل طلب الإذن إلا من خلال إجراء يتخذه المستخدم، مثل النقر على زر "حفظ".
بدلاً من ذلك، يمكن لتطبيق ويب يعدّل ملفات متعددة، مثل بيئة تطوير متكاملة (IDE)، أن يطلب أيضًا الإذن بحفظ التغييرات عند فتح الملفات.
إذا اختار المستخدم "إلغاء" ولم يمنح إذن الوصول للكتابة، لن يتمكّن تطبيق الويب من حفظ التغييرات في الملف المحلي. يجب أن يوفّر طريقة بديلة للمستخدم لحفظ بياناته، مثلاً من خلال توفير طريقة "لتنزيل" الملف أو حفظ البيانات في السحابة الإلكترونية.
الشفافية
بعد أن يمنح المستخدم إذنًا لتطبيق ويب بحفظ ملف محلي، يعرض المتصفّح رمزًا في شريط العناوين. يؤدي النقر على الرمز إلى فتح نافذة منبثقة تعرض قائمة بالملفات التي منح المستخدم إذن الوصول إليها. ويمكن للمستخدم في أي وقت إبطال هذا الإذن إذا أراد ذلك.
استمرار الإذن
يمكن لتطبيق الويب مواصلة حفظ التغييرات في الملف بدون طلب الإذن إلى أن يتم إغلاق جميع علامات التبويب الخاصة بمصدره. بعد إغلاق علامة التبويب، يفقد الموقع الإلكتروني إمكانية الوصول إلى كل البيانات. في المرة التالية التي يستخدم فيها المستخدم تطبيق الويب، سيُطلب منه مجددًا منح الإذن بالوصول إلى الملفات.
الملاحظات
نريد معرفة تجاربك مع File System Access API.
يُرجى إخبارنا عن تصميم واجهة برمجة التطبيقات
هل هناك أي شيء في واجهة برمجة التطبيقات لا يعمل على النحو المتوقّع؟ أو هل هناك طرق أو سمات مفقودة تحتاج إليها لتنفيذ فكرتك؟ هل لديك سؤال أو تعليق حول نموذج الأمان؟
- يمكنك الإبلاغ عن مشكلة في المواصفات على مستودع WICG File System Access GitHub، أو إضافة أفكارك إلى مشكلة حالية.
هل تواجه مشكلة في عملية التنفيذ؟
هل عثرت على خطأ في تنفيذ Chrome؟ أو هل يختلف التنفيذ عن المواصفات؟
- يمكنك الإبلاغ عن الخطأ على https://new.crbug.com. احرص على تضمين أكبر قدر ممكن من التفاصيل وتعليمات لإعادة إنتاج الخطأ، واضبط المكوّنات على
Blink>Storage>FileSystem.
هل تخطّط لاستخدام واجهة برمجة التطبيقات؟
هل تخطّط لاستخدام File System Access API على موقعك الإلكتروني؟ يساعدنا دعمك العلني في تحديد أولويات الميزات، ويوضّح لمورّدي المتصفّحات الآخرين مدى أهمية توفيرها.
- شارِك كيف تخطّط لاستخدامه في سلسلة محادثات WICG Discourse.
- أرسِل تغريدة إلى @ChromiumDev باستخدام الهاشتاغ
#FileSystemAccessوأخبِرنا عن مكان استخدامك لهذه الميزة وكيفية استخدامها.
روابط مفيدة
- شرح علني
- مواصفات الوصول إلى نظام الملفات ومواصفات الملف
- خطأ في التتبُّع
- إدخال ChromeStatus.com
- تعريفات TypeScript
- File System Access API - نموذج أمان Chromium
- مكوّن Blink:
Blink>Storage>FileSystem
الإقرارات
كتب مواصفات File System Access API Marijn Kruisselbrink.