قدرة المتصفحات على التعامل مع الملفات والأدلة لوقت طويل. File API توفر ميزات لتمثيل كائنات الملفات في تطبيقات الويب، بالإضافة إلى اختيارها آليًا والوصول إلى بياناتها. مع ذلك، في اللحظة التي تنظر فيها عن قرب، لا يكون كل ذلك بريقًا ذهبيًا.
الطريقة التقليدية للتعامل مع الملفات
فتح الملفات
يمكنك كمطور برامج فتح الملفات وقراءتها من خلال
<input type="file">
العنصر.
في أبسط صوره، يمكن أن يبدو فتح ملف مشابهًا لنموذج التعليمات البرمجية أدناه.
يمنحك الكائن input
السمة FileList
،
والتي في الحالة أدناه تتكون من قاعدة واحدة فقط
File
File
هو نوع معيّن من Blob
،
ويمكن استخدامها في أي سياق يمكن أن تفعله Blob.
const openFile = async () => {
return new Promise((resolve) => {
const input = document.createElement('input');
input.type = 'file';
input.addEventListener('change', () => {
resolve(input.files[0]);
});
input.click();
});
};
فتح الأدلة
لفتح المجلدات (أو الأدلة)، يمكنك ضبط
<input webkitdirectory>
.
بخلاف ذلك، تعمل جميع العناصر الأخرى بالطريقة نفسها كما هو موضح أعلاه.
على الرغم من أن اسم البائع الذي يبدأه البائع،
لا يمكن استخدام webkitdirectory
في Chromium وWebKit فحسب، بل يمكن استخدامه أيضًا في Edge القديم المستند إلى EdgeHTML وكذلك في Firefox.
حفظ الملفات (بدلاً من تنزيلها)
لحفظ ملف، تقتصر عادةً على تنزيل الملف،
والذي يعمل بفضل
<a download>
.
يمكنك ضبط السمة href
الخاصة بالارتساء على عنوان URL للسمة blob:
يمكنك الحصول عليه من خلال
URL.createObjectURL()
.
const saveFile = async (blob) => {
const a = document.createElement('a');
a.download = 'my-file.txt';
a.href = URL.createObjectURL(blob);
a.addEventListener('click', (e) => {
setTimeout(() => URL.revokeObjectURL(a.href), 30 * 1000);
});
a.click();
};
المشكلة
ومن الجانب السلبي الكبير في أسلوب التنزيل أنه ليست هناك طريقة لإنشاء ملف فتح ← تعديل ← حفظ الإجراء، أي أنه لا توجد طريقة لاستبدال الملف الأصلي. بدلاً من ذلك، ستحصل على نسخة جديدة من الملف الأصلي. في مجلد Downloads (التنزيلات) التلقائي لنظام التشغيل كلما اخترت "الحفظ".
واجهة برمجة التطبيقات File System Access API
تجعل واجهة برمجة التطبيقات File System Access API كلاً من العمليات والفتح والحفظ أسهل كثيرًا. ويتيح ذلك أيضًا الحفظ الصحيح، أي لا يمكنك فقط اختيار مكان حفظ الملف، ولكن أيضًا استبدال ملف حالي.
فتح الملفات
باستخدام File System Access API،
يعتمد فتح ملف على استدعاء واحد لطريقة window.showOpenFilePicker()
.
تعرض هذه الاستدعاء مؤشر ملف يمكنك من خلاله الحصول على File
الفعلي بطريقة getFile()
.
const openFile = async () => {
try {
// Always returns an array.
const [handle] = await window.showOpenFilePicker();
return handle.getFile();
} catch (err) {
console.error(err.name, err.message);
}
};
فتح الأدلة
يمكنك فتح دليل من خلال الاتصال
window.showDirectoryPicker()
يؤدي إلى إتاحة إمكانية اختيار الأدلة في مربّع الحوار للملف.
حفظ الملفات
وبالمثل، فإن حفظ الملفات أمر واضح تمامًا.
من اسم معرِّف ملف، يمكنك إنشاء بث قابل للكتابة عبر createWritable()
،
ثم تكتب بيانات Blob عن طريق استدعاء طريقة write()
للبث،
وأخيرًا، يتم إغلاق البث من خلال استدعاء طريقة close()
.
const saveFile = async (blob) => {
try {
const handle = await window.showSaveFilePicker({
types: [{
accept: {
// Omitted
},
}],
});
const writable = await handle.createWritable();
await writable.write(blob);
await writable.close();
return handle;
} catch (err) {
console.error(err.name, err.message);
}
};
لمحة عن متصفّح-fs-access
تمامًا مثل واجهة برمجة تطبيقات File System Access API، أنها ليست متاحة على نطاق واسع بعد.
ولهذا السبب، أرى أن واجهة File System Access API هي تحسين تدريجي. وبالتالي، أريد استخدامها عندما يتيح المتصفّح ذلك، واستخدام النهج التقليدي إن لم يكن كذلك؛ وكل ذلك مع عدم معاقبة المستخدم بعمليات تنزيل غير ضرورية لرمز JavaScript غير متوافق. يعمل متصفّح browser-fs-access والمكتبة هي ردي على هذا التحدي.
فلسفة التصميم
ونظرًا لأنه من المحتمل أن تتغير واجهة برمجة تطبيقات File System Access API في المستقبل،
لم يتم تصميم واجهة برمجة تطبيقات browser-fs-access بعد ذلك.
وهذا يعني أنّ المكتبة ليست polyfill،
بل بالأحرى عبارة عن قصة قصيرة.
يمكنك (بشكل ثابت أو ديناميكي) استيراد أي وظائف تحتاجها بشكل حصري من أجل إبقاء تطبيقك صغيرًا قدر الإمكان.
الطرق المتاحة يتم تسميتها بشكل مناسب
fileOpen()
،
directoryOpen()
و
fileSave()
داخليًا، تكتشف ميزة المكتبة ما إذا كانت واجهة برمجة التطبيقات File System Access API متوافقة،
ثم استيراد مسار الرمز المقابل.
استخدام مكتبة المتصفّح-fs-access
من السهل استخدام الطرق الثلاث.
يمكنك تحديد mimeTypes
أو الملف extensions
المقبولَين لتطبيقك، وضبط علامة multiple
.
للسماح بتحديد عدة ملفات أو أدلة أو عدم السماح بتحديدها.
للحصول على التفاصيل الكاملة، يمكنك مراجعة
مستندات واجهة برمجة التطبيقات Browser-fs-access
يوضح نموذج التعليمات البرمجية أدناه كيفية فتح ملفات الصور وحفظها.
// The imported methods will use the File
// System Access API or a fallback implementation.
import {
fileOpen,
directoryOpen,
fileSave,
} from 'https://unpkg.com/browser-fs-access';
(async () => {
// Open an image file.
const blob = await fileOpen({
mimeTypes: ['image/*'],
});
// Open multiple image files.
const blobs = await fileOpen({
mimeTypes: ['image/*'],
multiple: true,
});
// Open all files in a directory,
// recursively including subdirectories.
const blobsInDirectory = await directoryOpen({
recursive: true
});
// Save a file.
await fileSave(blob, {
fileName: 'Untitled.png',
});
})();
عرض توضيحي
يمكنك رؤية الرمز أعلاه وهو قيد التطبيق في عرض توضيحي لأداة Glitch. وبالمثل، يتوفر رمز المصدر هناك. نظرًا لعدم السماح للإطارات الفرعية من مصادر متعددة بعرض أداة اختيار الملفات، فهذا ليس مسموحًا به، لا يمكن تضمين العرض التوضيحي في هذه المقالة.
مكتبة متصفّح fs-access متاحة في البرية
في وقت فراغي، أساهم قليلاً في تطبيق ويب تقدّمي (PWA) قابل للتثبيت واسمه ExcaliDraw، أداة سبورة تتيح لك رسم المخططات البيانية بسهولة بطريقة مرسومة باليد. وهو مستجيب بالكامل ويعمل بشكل جيد على مجموعة من الأجهزة بدءًا من الهواتف المحمولة الصغيرة إلى أجهزة الكمبيوتر ذات الشاشات الكبيرة. هذا يعني أنها بحاجة إلى التعامل مع الملفات على جميع الأنظمة الأساسية المختلفة ما إذا كانت متوافقة مع واجهة برمجة التطبيقات File System Access API أم لا. وهذا يجعله مرشحًا رائعًا لمكتبة web-fs-access.
يمكنني، على سبيل المثال، بدء رسم على هاتف iPhone، حفظه (من الناحية الفنية: التنزيل، لأنّ Safari لا يتوافق مع File System Access API) إلى مجلد عمليات التنزيل على iPhone، وفتح الملف على سطح المكتب (بعد نقله من هاتفي)، أو تعديل الملف واستبداله بالتغييرات التي أجريتها، أو حتى حفظه كملف جديد.
عيّنة من التعليمات البرمجية الواقعية
يمكنك الاطّلاع أدناه على مثال حقيقي لإذن الوصول إلى المتصفِّح (fs-access) كما هو مُستخدَم في ExcaliDraw.
هذا المقتطف مأخوذ من
/src/data/json.ts
تشكّل الطريقة saveAsJSON()
أهمية خاصة في ما يتعلّق بتمرير مؤشر الملف أو null
إلى browser-fs-access.
fileSave()
، تؤدي إلى استبدالها عند توفير اسم معرِّف،
أو حفظه في ملف جديد إذا لم يكن كذلك.
export const saveAsJSON = async (
elements: readonly ExcalidrawElement[],
appState: AppState,
fileHandle: any,
) => {
const serialized = serializeAsJSON(elements, appState);
const blob = new Blob([serialized], {
type: "application/json",
});
const name = `${appState.name}.excalidraw`;
(window as any).handle = await fileSave(
blob,
{
fileName: name,
description: "Excalidraw file",
extensions: ["excalidraw"],
},
fileHandle || null,
);
};
export const loadFromJSON = async () => {
const blob = await fileOpen({
description: "Excalidraw files",
extensions: ["json", "excalidraw"],
mimeTypes: ["application/json"],
});
return loadFromBlob(blob);
};
اعتبارات واجهة المستخدم
سواء كنت تستخدم ExcaliDraw أو في تطبيقك
يجب أن تتكيّف واجهة المستخدم مع وضع دعم المتصفح.
إذا كانت واجهة برمجة التطبيقات File System Access API متوافقة (if ('showOpenFilePicker' in window) {}
)
يمكنك عرض زر حفظ باسم بالإضافة إلى الزر حفظ.
توضح لقطات الشاشة أدناه الفرق بين شريط أدوات التطبيق الرئيسي سريع الاستجابة في ExcaliDraw على iPhone وعلى Chrome على أجهزة الكمبيوتر المكتبي.
لاحظ كيف أن الزر حفظ باسم غير موجود على iPhone.
الاستنتاجات
يمكن التعامل مع ملفات النظام من الناحية الفنية على جميع المتصفحات الحديثة. في المتصفحات التي تتيح استخدام واجهة برمجة التطبيقات File System Access API، يمكنك تحسين التجربة من خلال السماح للحفظ والاستبدال الفعلي (وليس فقط التنزيل) للملفات من خلال السماح للمستخدمين بإنشاء ملفات جديدة أينما أرادوا، كل ذلك مع الحفاظ على استخدامها في المتصفحات التي لا تتوافق مع واجهة برمجة التطبيقات File System Access API. متصفّح browser-fs-access يسهّل حياتك من خلال التعامل مع التفاصيل الدقيقة للتحسين التدريجي وجعل التعليمة البرمجية بسيطة قدر الإمكان.
شكر وتقدير
تمت مراجعة هذه المقالة بواسطة جو ميدلي و كايس باسك. نتوجّه بالشكر إلى مساهمين في ExcaliDraw عن عمله في المشروع ومراجعة طلبات السحب. الصورة الرئيسية من تصميم إيليا بافلوف على موقع Unspark.