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