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