إضافة الوصول إلى الملفات في كوردوفا

من موسوعة حسوب

تقدم إضافة الملفات (cordova-plugin-file) واجهة برمجية للملفات، والتي يمكن استخدامها للقراءة أو الكتابة في الملفات الموجودة على الجهاز.

هذه الإضافة مبنية على عدة مواصفات، بما فيها:

ملاحظة: رغم أن مواصفات W3C FileSystem صارت مُتجاوزة في متصفحات الويب، إلا أنّ كوردوفا تدعمها في هذه الإضافة في المنصات المدرجة في فقرة المنصات المدعومة، باستثناء المتصفحات (Browser).

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

للتعرف على خيارات التخزين الأخرى، يرجى الرجوع إلى دليل التخزين.

تعرّف إضافة الملفات كائنًا عامًّا cordova.file. وعلى الرغم من أنّ هذا الكائن موجود في النطاق العام (global scope)، إلا أنه لن يكون متوفرًا إلا بعد إطلاق الحدث deviceready.

document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
    console.log(cordova.file);
}

التثبيت

يمكنك تثبيت هذه الإضافة عبر الأمر:

cordova plugin add cordova-plugin-file

المنصات المدعومة

  • أندرويد
  • iOS
  • OS X
  • ويندوز
  • Browser

لا تدعم هذه المنصات التابعين FileReader.readAsArrayBuffer أو FileWriter.write(blob)‎.

مكان تخزين الملفات (Where to Store Files)

بدءًا من الإصدار 1.2.0، صار استخدام العناوين (URL) لمجلدات نظام الملفات المهمة مسموحًا. العناوين تكون على الشكل: file:///path/to/spot/‎، ويمكن تحويلها إلى DirectoryEntry باستخدام window.resolveLocalFileSystemURL().

  • cordova.file.applicationDirectory - المجلد حيث ثُبِّت التطبيق، وهو للقراءة فقط. (iOS، أندرويد، BlackBerry 10، OSX، ويندوز)
  • cordova.file.applicationStorageDirectory - المجلد الجذري لصندوق للتطبيق (application's sandbox)؛ على منصتي iOS وويندوز، يكون هذا المجلد للقراءة فقط (لكن بعض المجلدات الفرعية المحددة [مثل /Documents في iOS أو /localState في ويندوز] قابلة للقراءة والكتابة). جميع البيانات المضمنة داخل هذا المجلد ستبقى خاصة بالتطبيق. (iOS أندرويد و BlackBerry 10 و OSX)
  • cordova.file.dataDirectory - مجلد لتخزين البيانات الدائم والخاص داخل صندوق التطبيق (application's sandbox) باستخدام الذاكرة الداخلية (على منصة أندرويد، إن كنت بحاجة إلى استخدام ذاكرة خارجية، فاستخدم .externalDataDirectory). على منصة iOS، لا يُزامن هذا المجلد مع السحابة iCloud (استخدم .syncedDataDirectory). (ويندوز، أندرويد، iOS و BlackBerry 10)
  • cordova.file.cacheDirectory - مجلد لملفات البيانات المخزنة مؤقتًا، أو أيّ ملف يمكن للتطبيق إعادة إنشائه بسهولة. قد يحذف نظام التشغيل هذه الملفات عندما يشعر بانحسار ذاكرة الجهاز، ومع ذلك، يجب ألا تعتمد التطبيقات على نظام التشغيل لحذف الملفات الموجودة في هذا المجلد. (iOS، أندرويد، BlackBerry 10، OSX، ويندوز)
  • cordova.file.externalApplicationStorageDirectory - مساحة التطبيق على وحدة التخزين الخارجية. (أندرويدت)
  • cordova.file.externalDataDirectory - المجلد الذي ستوضع فيه الملفات التي تحتوي البيانات الخاصة بالتطبيق على وحدة التخزين الخارجية. (أندرويد)
  • cordova.file.externalCacheDirectory - ذاكرة التخزين المؤقت للتطبيق في وحدة التخزين الخارجية. (أندرويد)
  • cordova.file.externalRootDirectory - المجلد الجذري (root) لوحدة التخزين الخارجية (بطاقة SD). (أندرويد، BlackBerry 10)
  • cordova.file.tempDirectory - مجلد يضم الملفات المؤقتة التي يمكن لنظام التشغيل مسحها في أي وقت. لا تعتمد على نظام التشغيل لمحو محتويات هذا المجلد. يجب أن يزيل تطبيقك دائمًا الملفات الموجودة في هذا التطبيق بحسب الاقتضاء. (iOS، OSX، ويندوز)
  • cordova.file.syncedDataDirectory - يحتوي هذا المجلد ملفات التطبيق التي يجب مزامنتها (مثلًا، مع السحابة iCloud). (ويندوز، iOS)
  • cordova.file.documentsDirectory - يضم هذا المجلد الملفات الخاصة بالتطبيق، ولكن ذات الأهمية للتطبيقات الأخرى (مثل ملفات Office). لاحظ أنه في منصة OSX، فهذا المجلد هو مجلد المستخدم ~/Documents. ‏(iOS، OSX)
  • cordova.file.sharedDirectory - يحتوي هذا المجلد على الملفات المتاحة لجميع التطبيقات (BlackBerry 10)


بنية نظام الملفات (File System Layouts)

على الرغم من أنها من الناحية الفنية تُعد من تفاصيل التقديم (implementation)، إلا أنه قد يكون من المفيد معرفة كيفية تعيين خصائص cordova.file.* للمسارات الفعلية على الجهاز الحقيقي.

بنية نظام الملفات في منصة iOS

مسار الجهاز cordova.file.* iosExtraFileSystems كتابة/قراءة؟ مستمرة؟ أيمحوه نظام التشغيل؟ مزامنة خاص /var/mobile/Applications/<UUID>/ applicationStorageDirectory - r N/A N/A N/A نعم appname.app/ applicationDirectory bundle r N/A N/A N/A نعم www/ - - r N/A N/A N/A نعم Documents/ documentsDirectory مستندات ق/ك نعم لا نعم نعم NoCloud/ - documents-nosync ق/ك نعم لا لا نعم Library - library ق/ك نعم لا نعم؟ نعم NoCloud/ dataDirectory library-nosync ق/ك نعم لا لا نعم Cloud/ syncedDataDirectory - ق/ك نعم لا نعم نعم Caches/ cacheDirectory cache ق/ك نعم* نعم*** لا نعم tmp/ tempDirectory - ق/ك لا** نعم*** لا نعم

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

بنية نظام الملفات في أندرويد

مسار الجهاز cordova.file.* AndroidExtraFileSystems كتابة/قراءة؟ مستمرة؟ أيمحوه نظام التشغيل؟ خاص file:///android_asset/ applicationDirectory assets r N/A N/A نعم /data/data/<app-id>/ applicationStorageDirectory - ق/ك N/A N/A نعم cache cacheDirectory cache ق/ك نعم نعم* نعم files dataDirectory files ق/ك نعم لا نعم Documents

documents ق/ك نعم لا نعم <sdcard>/ externalRootDirectory sdcard ق/ك نعم لا لا Android/data/<app-id>/ externalApplicationStorageDirectory - ق/ك نعم لا لا cache externalCacheDirectory cache-external ق/ك نعم لا** لا files externalDataDirectory files-external ق/ك نعم لا لا

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

Note: إذا تعذر تخزين وحدة التخزين الخارجية، ستكون قيمة الخاصيات cordova.file.external* مساوية للقيمة null.

بنية ملفات منصة OS X

مسار الجهاز cordova.file.* iosExtraFileSystems كتابة/قراءة؟ أيمحوه نظام التشغيل؟ خاص /Applications/<appname>.app/ - bundle r N/A نعم Content/Resources/ applicationDirectory - r N/A نعم ~/Library/Application Support/<bundle-id>/ applicationStorageDirectory - ق/ك لا نعم files/ dataDirectory - ق/ك لا نعم ~/Documents/ documentsDirectory documents ق/ك لا لا ~/Library/Caches/<bundle-id>/ cacheDirectory cache ق/ك لا نعم /tmp/ tempDirectory - ق/ك نعم* نعم / rootDirectory root ق/ك لا** لا

Note: هذه بنيو التطبيقات غير المحصنة (non sandboxed applications). إذا قمت بتحصين (sandboxing) التطبيق، فسوف المجلد applicationStorageDirectory داخل المجلد ~/Library/Containers/<bundle-id>/Data/Library/Application Support.

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

بنية نظام ملفات ويندوز

مسار الجهاز cordova.file.* كتابة/قراءة؟ مستمرة؟ أيمحوه نظام التشغيل؟ خاصة ms-appdata:/// applicationDirectory r N/A N/A نعم local/ dataDirectory ق/ك نعم لا نعم temp/ cacheDirectory ق/ك لا نعم* نعم temp/ tempDirectory ق/ك لا نعم* نعم roaming/ syncedDataDirectory ق/ك نعم لا نعم

  • قد يمحو نظام التشغيل هذا المجلد بشكل دوري

ملاحظات خاصة بمنصة أندرويد

موضع التخزين الدائم في أندرويد

هناك عدة مواضع صالحة لتخزين الملفات بشكل دائم على أجهزة أندرويد . راجع this page للحصول على معلومات مستفيضة بخصوص مختلف الإمكانيات.

كانت الإصدارات السابقة من هذه الإضافة تختار موضع الملفات المؤقتة والمستقرة عند بدء التشغيل، استنادًا إلى ما تم تركيب بطاقة الذاكرة SD (أو أي أداة تخزين مكافئة) على الجهاز أم لا. إن رُكِّبت بطاقة الذاكرة SD، أو إن توفرت مساحة تخزين داخلية (على أجهزة Nexus مثلاً)، فسيتم تخزين الملفات المستقرة في المجلد الجذري لتلك المساحة. هذا يعني أن جميع تطبيقات كورودوفا ستكون قادرة على أن ترى جميع الملفات المتوفرة على البطاقة.

في الإصدارات السابقة، إذا لم تكن بطاقة الذاكرة SD متوفرة، فستُخزّن البيانات ضمن المجلد /data/data/<packageId>، والذي يعزل التطبيقات عن بعضها البعض، ولكن قد يسمح بتشارك البيانات بين المستخدمين.

من الممكن الآن اختيار تخزين الملفات في الملفات الداخلية، أو استخدام المقاربة السابقة، يمكنك فعل هذا عبر الوسم preference في ملف التطبيق config.xml. عبر إضافة أحد هذين السطرين إلى config.xml:

<preference name="AndroidPersistentFileLocation" value="Internal" />
<preference name="AndroidPersistentFileLocation" value="Compatibility" />

بدون هذا السطر، ستستخدم الإضافة Internal كإعداد افتراضي. في حال كان الوسم preference حاضرًا، ولم يكن يساوي إحدى هذه القيم، فلن يعمل التطبيق.

إذا سبق وأرسلت تطبيقك إلى المستخدمين باستخدام إصدار قديم (قبل الإصدار 3.0.0) من هذه الإضافة، وكان التطبيق قد خزّن الملفات في نظام الملفات الدائمة (persistent filesystem)، فيجب تعيين preference عند القيمة Compatibility إذا لم يحدد الملف config.xml موضع نظام الملفات الدائمة. قد يؤدي تبديل موضع التخزين وجعله "داخليا" (Internal) إلى عدم استطاعة المستخدمين الحاليين الذين يقومون بترقية تطبيقاتهم الوصول إلى ملفاتهم المخزنة مسبقًا، وذلك اعتمادًا على الجهاز المستخدم.

إذا كان تطبيقك جديدًا، أو لم يسبق له تخزين الملفات في نظام الملفات الدائمة، فيفضل عمومًا استخدام الإعداد Internal.

العمليات العودية البطيئة على المجلد ‎/android_asset

العمل على مجلدات الأصول (asset directories) بطيئة جدا في أندرويد. يمكنك تسريع ذلك عن طريق إضافة src/android/build-extras.gradle إلى جذر مشروع أندرويد (يتطلب الإصدار cordova-android@4.0.0 أو ما بعده).

الإذن في الكتابة في وحدة التخزين الخارجية عندما لا يتم تركيبه على منصة Marshmallow

نتطلب منصة Marshmallow أن تطلب التطبيقات أذونات عند القراءة أو الكتابة من أو في مواقع خارجية. افتراضيًا default، يملك تطبيقك الإذن في الكتابة في cordova.file.applicationStorageDirectory و cordova.file.externalApplicationStorageDirectory، ولن يكون على الإضافة طلب إذن للكتابة في هذين المجلدين إلا إذا لم يتم تركيب وحدة تخزين خارجية. ولكن نظرًا لبعض الإكراهات، إذا لم تُركّب وحدة التخزين الخارجية، فستطلب الإذن للكتابة في cordova.file.externalApplicationStorageDirectory.

ملاحظات خاصة بمنصة IOS

  • cordova.file.applicationStorageDirectory للقراءة فقط؛ ستفشل أي محاولة لتخزين الملفات داخل المجلد الجذري. استخدم أحد الخاصيات cordova.file.* الأخرى المحددة لأجل منصة iOS (وحدهما المجلدان applicationDirectory و applicationStorageDirectory للقراءة فقط).
  • FileReader.readAsText(blob, encoding)
  • المعامل encoding غير مدعوم، والترميز UTF-8 هو المعتمد دائمًا.

موضع التخزين الدائم في منصة iOS

هناك موضعان يصلحان لتخزين الملفات الدائمة على جهاز iOS: مجلد المستندات (Documents directory) ومجلد المكتبة (Library directory). الإصدارات السابقة من الإضافة كانت تخزّن الملفات الدائمة خصرًا في مجلد المستندات. وهذا جعل جميع ملفات التطبيق مرئية في تطبيق iTunes، وهو ما لم يكن مقصودًا غالبًا، خاصة للتطبيقات التي تتعامل مع الكثير من الملفات الصغيرة، بدلاً من إنتاج مستندات كاملة لتصديرها، وهو الغرض المقصود من المجلد.

من الممكن الآن اختيار تخزين الملفات في مجلد المستندات أو مجلد المكتبة، عبر الوسم preference في الملف config.xml الخاص بتطبيقك. عبر إضافة أحد هذين السطرين إلى config.xml:

<preference name="iosPersistentFileLocation" value="Library" />
<preference name="iosPersistentFileLocation" value="Compatibility" />

بدون هذا السطر، ستستخدم الإضافة Compatibility كإعداد افتراضي. في حال كان الوسم preference حاضرًا، ولم يكن يساوي إحدى هذه القيم، فلن يعمل التطبيق.

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

إن كان تطبيقك جديدًا، أو لم يسبق له تخزين الملفات في نظام الملفات الدائمة، فيفضل عمومًا استخدام الإعداد Library.

ملاحظات خاصة بالمتصفحات (Browsers)

ملاحظات خاصة وتوضيحات

  • يستخدم كل متصفح نظام ملفات محصّن (sandboxed) خاص به. يستخدم المتصفحان IE و فايرفوكس نظام الملفات IndexedDB كأساس. وتستخدم جميع المتصفحات عارضة مائلة للأمام كفاصلة داخل المسار.
  • يجب إنشاء مدخلات المجلد على التوالي. على سبيل المثال، سيفشل استدعاء التابع fs.root.getDirectory('dir1/dir2', {create:true}, successCallback, errorCallback) إذا لم يكن المجلد dir1 موجودًا.
  • تستأذن الإضافة من المستخدم لاستخدام التخزين الدائم عند بدء التطبيق لأول مرة.
  • تدعم الإضافة cdvfile://localhost (الموارد المحلية) فقط. أي أنّ cdvfile لا يدعم الموارد الخارجية.
  • لا تتقيّد الإضافة "File System API 8.3 Naming restrictions".
  • الكائن Blob و تابع الصنف File ‏ close غير مدعومان.
  • لا تدعم هذه الإضافة FileSaver و BlobBuilder وليس لهما أكواد مُجتزأة (stubs).
  • لا تدعم الإضافة requestAllFileSystems. هذه الدالة غير موجودة في المواصفات أيضًا.
  • لن تتم إزالة المُدخلات في المجلد إن كنت تستخدم الراية create: true مع مجلد موجود.
  • الملفات المُنشأة عبر الباني (constructor) ليست مدعومة. عليك استخدام التابع entry.file بدلاً من ذلك.
  • يستخدم كل متصفح شكلًا خاصًا من عناوين blob المرجعية.
  • الدالة readAsDataURL مدعومة، لكن نوع الوسيط (mediatype) في متصفح كروم يعتمد على امتداد اسم المُدخلات (entry name extension)، نوع الوسيط mediatype في المتصفح IE دائمًا فارغ (وهو أمر مماثل لـ text-plain وفقًا للمواصفات)، أما في متصفح فايرفوكس فإنّ mediatype تساوي دائمًا application/octet-stream. على سبيل المثال، إن كان المحتوى هو abcdefg، فحينئذٍ سيعيد المتصفح فايرفوكس القيمة data:application/octet-stream;base64,YWJjZGVmZw==، وسيعيد المتصفح IE القيمة data:;base64,YWJjZGVmZw==، فيما سيعيد المتصفح كروم القيمة data:<mediatype depending on extension of entry name>;base64,YWJjZGVmZw==.
  • تُعيد الخاصية toInternalURL المسار وفق الصيغة file:///persistent/path/to/entry (على فايرفوكس و IE). فيما يعيد كروم المسار وفق الصيغة cdvfile://localhost/persistent/file.

ملاحظات خاصة بمتصفح كروم

  • نظام الملفات في متصفح كروم لا يكون جاهزًا فور إطلاق الحدث حدث ready الخاص بالجهاز. كحل بديل، يمكنك الاشتراك في الحدث filePluginIsReady. مثال: javascript

window.addEventListener('filePluginIsReady', function(){ console.log('File plugin is ready');}, false); يمكنك استخدام الدالة window.isFilePluginReadyRaised للتحقق مما إن كان الحدث قد أُطلِق بالفعل.

  • حصة نظام الملفات window.requestFileSystem المؤقت (TEMPORARY) والدائم (PERSISTENT) غير محدودة في كروم.
  • لزيادة سعة التخزين الدائم في كروم، يلزمك استدعاء التابع window.initPersistentFileSystem. تبلغ حصة التخزين الدائم 5 ميغابايت بشكل افتراضي.
  • يتطلب كروم تمرير الوسيط --allow-file-access-from-files إلى الأمر run لدعم الواجهة البرمجية (API) عبر البروتوكول file:///.
  • لن يُغيّر الكائن File إن كنت تستخدم الراية {create:true} عند الحصول على مدخل Entry موجود.
  • تُضبط خاصية الأحداث cancelable عند القيمة true في متصفح كروم. على خلاف specification.
  • تعيد الدالة toURL في كروم مسارًا مسبوقا بالبادئة filesystem: بناءً على التطبيق المُضيف. على سبيل المثال، filesystem:file:///persistent/somefile.txt، filesystem:http://localhost:8080/persistent/somefile.txt.
  • لا تحتوي نتيجة الدالة toURL على شرطة مائلة في حالة إدخال المجلد. مع ذلك، يحُل كروم المجلدات ذات العناوين (url) التي تنتهي بشرطة مائلة بشكلٍ صحيح.
  • يتطلب التابع resolveLocalFileSystemURL أن يكون للعنوان الخلفي url بادئة filesystem. على سبيل المثال، يجب أن يكون المعامل url الخاص بالتابع resolveLocalFileSystemURL وفق الصيغة filesystem:file:///persistent/somefile.txt بدلاً من الصيغة file:///persistent/somefile.txt المعمول بها في أندرويد.
  • الدالة toNativeURL ليست مدعومة، وليس لديها كعب (stub).
  • الدالة setMetadata ليست مذكورة في المواصفات وليست مدعومة.
  • يُطلق الخطأ INVALIDMODIFICATIONERR (الكود: 9) بدلاً من الخطأ SYNTAX_ERR (الكود: 8) عند طلب (requesting) نظام ملفات غير موجود.
  • يُطلق الخطأ INVALIDMODIFICATIONERR (الكود: 9) بدلاً من الخطأ PATHEXISTSERR (الكود: 12) عند محاولة إنشاء ملف أو مجلد، موجود مسبقاً.
  • يُطلق الخطأ INVALIDMODIFICATIONERR (الكود: 9) بدلاً من الخطأ NOMODIFICATIONALLOWED_ERR (الكود: 6) عند محاولة استدعاء removeRecursively على المجلد الجذري (root) نظام الملفات.
  • يُطلق الخطأ INVALIDMODIFICATIONERR (الكود: 9) بدلاً من الخطأ NOTFOUNDERR (الكود: 1) عند محاولة الانتقال إلى مجلد غير موجود.

ملاحظات خصاصة بالمتصفحات التي تستخدم IndexedDB (فايرفوكس و IE)

  • الصيغتان . و .. غير مدعومتان.
  • لا يدعم المتصفح IE الوضع file:///؛ حيث دعم الوضع المستضاف فقط (http://localhost:xxxx).
  • حجم نظام الملفات في فايرفوكس غير محدود، ولكن عند كل تمديد للمساحة بـ 50 ميغابايت، فسيُطلب إذن المستخدم. يتيح المتصفح IE10 ما يصل إلى 10 ميغابايت من AppCache و IndexedDB المُستخدمين في تقيدم نظام الملفات دون المطالبة بالإذن، بمجرد أن تصل إلى هذا المستوى، سيُطلب منك إن كنت تريد السماح بزيادة هذا الحجم إلى 250 ميغابايت كحد أقصى لكل موقغ. لذلك لا يؤثر المعامل size الخاص بالدالة requestFileSystem في نظام الملفات في المتصفحين فايرفوكس و IE.
  • الدالة readAsBinaryString لم تُذكر في المواصفات، وليست مدعومة في المتصفح IE وليس لها تحتوي جذع (stub).
  • file.type تساوي دائما null.
  • عليك ألا تنشئ مُدخلات باستخدام نتيجة دالة الاستجابة (callback) لنسخة DirectoryEntry المحذوفة. وإلا فستحصل على "مُدخلة معلقة" (hanging entry).
  • قبل أن تتمكن من قراءة ملف مكتوب للتو، عليك أن تحصل أولا على نسخة جديدة من ذلك الملف.
  • الدالة setMetadata، والتي لم تُذكر في المواصفات، تدعم تغيير الحقل modificationTime فقط.
  • لا تدعم الدالتان copyTo و moveTo المجلدات.
  • البيانات الوصفية (metadata) للمجلدات غير مدعومة.
  • لا يفشل التابعان Entry.remove و directoryEntry.removeRecursively عند إزالة المجلدات غير الفارغة - إذ يتم تنظيف المجلدات المُزالة مع محتوياتها.
  • الدالتان abort و truncate غير مدعومتان.
  • لا يتم إطلاق الأحداث progress. على سبيل المثال، لن يُنفّذ هذا المعالج: javascript

writer.onprogress = function() { /*commands*/ };

ملاحظات حول الترقية

في الإصدار 1.0.0 من هذه الإضافة، تغيرت بِنيتَا FileEntry و DirectoryEntry، لتكون أكثر انسجاما مع المواصفات المنشورة.

الإصدارات السابقة (قبل 1.0.0) من الإضافة كانت تُخزن device-absolute-file-location في الخاصية fullPath في كائنات Entry. هذه المسارات تبدو عادة كالتالي:

/var/mobile/Applications/<application UUID>/Documents/path/to/file  (iOS)
/storage/emulated/0/path/to/file                                    (Android)‎

تُعاد هذه المسارات أيضًا بواسطة التابع toURL() الخاص بالكائنات Entry.

في الإصدار v1.0.0، تمثل الخاصية fullPath مسار الملف، نسبةً إلى المجلد الجذري لنظام ملفات HTML. لذلك، سيتم الآن تمثيل كل المسارات المذكورة أعلاه عن طريق الكائن FileEntry كمسار كامل fullPath كالتالي:

/path/to/file‎

إن كان تطبيقك يعمل مع device-absolute-paths، وقمت سابقًا باسترداد هذه المسارات عبر الخاصية fullPath من كائنات Entry، فعليك تحديث شيفرتك البرمجية لاستخدام entry.toURL() بدلاً من ذلك.

للتوافق مع الإصدارات القديمة، سيقبل التابع resolveLocalFileSystemURL() المعامل device-absolute-path، وسيُعيد كائنًا Entry مقابلًا له، طالما أن الملف موجود داخل أحد نظامي الملفات TEMPORARY أو PERSISTENT.

كانت هذه مشكلة خاصة بالإضافة File-Transfer، والتي كانت تستخدم سابقًا مسارات مطلقة للجهاز device-absolute-paths (ولا تزال تقبلها). لقد تم تحديثه لتعمل بشكل صحيح مع روابط نظام الملفات FileSystem URLs، لذا فاستبدال entry.fullPath بـ entry.toURL() سيحل أية مشاكل متعلقة بالإضافة فيما يخص العمل على الملفات الموجودة على الجهاز.

في الإصدار 1.1.0، تم تغيير القيمة المعادة من الدالة toURL() (انظر CB-6394)، إذ باتت تعيد الرابط المطلق "file://‎" ما أمكن. للحصول على رابط داخلي ‎'cdvfile:'‎، يمكنك استخدام التابع toInternalURL(). سيعيد هذه التابع الآن عناوين نظام الملفات وفق الصيغة التالية:

cdvfile://localhost/persistent/path/to/file‎

والتي يمكن استخدامها لتحديد الملف بدقة.

البروتوكول cdvfile

Purpose

يمكن استخدام cdvfile://localhost/persistent|temporary|another-fs-root*/path/to/file في مسارات الملفات المستقلة عن المنصات. مسارات cdvfile مدعومة من قبل الإضافات الأساسية (core plugins)- على سبيل المثال، يمكنك تنزيل ملف mp3 إلى مسار cdvfile عبر الإضافة cordova-plugin-file-transfer ثم تشغيلها عبر الإضافة cordova-plugin-media.

*Note: راجع [#where-to-store-files Where to Store Files] و [#file-system-layouts File System Layouts] و [#configuring-the-plugin-optional Configuring the Plugin] لمزيد من التفاصيل حول جذور نظام المفات المتاحة.

لاستخدام cdvfile كوسم src يمكنك تحويله إلى مسار أصلي عبرالتابع toURL() الخاص بالكائن fileEntry الناتج، والذي يمكنك الحصول عليه عبر resolveLocalFileSystemURL - راجع الأمثلة أدناه.

يمكنك أيضًا استخدام مسارات cdvfile:// مباشرةً داخل الشِّعب (DOM)، على سبيل المثال:

<img src="cdvfile://localhost/persistent/img/logo.png" />

Note: يتطلب هذا التابع تحديثات الأمان التالية:

  • أضف نظام cdvfile: إلى الوسم الوصفي Content-Security-Policy في صفحة الفهرس، على سبيل المثال:
  • <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap:cdvfile:https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">
  • أضف <access origin="cdvfile://*" /> إلى config.xml.

Converting cdvfile:// to native path

resolveLocalFileSystemURL('cdvfile://localhost/temporary/path/to/file.mp4', function(entry) {
    var nativePath = entry.toURL();
    console.log('Native URI: ' + nativePath);
    document.getElementById('video').src = nativePath;

Converting native path to cdvfile://

resolveLocalFileSystemURL(nativePath, function(entry) {
    console.log('cdvfile URI: ' + entry.toInternalURL());

Using cdvfile in core plugins

fileTransfer.download(uri, 'cdvfile://localhost/temporary/path/to/file.mp3', function (entry) { ...
var my_media = new Media('cdvfile://localhost/temporary/path/to/file.mp3', ...);
my_media.play();

ملاحظات خاصة بنظام الملفات البروتوكول cdvfile

  • استخدام مسارات cdvfile:// في الشِّعب (DOM) غير مدعوم في منصة ويندوز (يمكن تحويل المسار إلى مسار أصلي بدلاً من ذلك).

لائحة أكواد الأخطاء ومعانيها

عند إطلاق خطأ، سيتم استخدام أحد الأكواد التالية. كود ثابتة 1 NOT_FOUND_ERR 2 SECURITY_ERR 3 ABORT_ERR 4 NOT_READABLE_ERR 5 ENCODING_ERR 6 NO_MODIFICATION_ALLOWED_ERR 7 INVALID_STATE_ERR 8 SYNTAX_ERR 9 INVALID_MODIFICATION_ERR 10 QUOTA_EXCEEDED_ERR 11 TYPE_MISMATCH_ERR 12 PATH_EXISTS_ERR

تعديل إعدادات الإضافة (اختياري)

يمكن تعديل إعدادات أنظمة الملفات المتاحة لكل منصة على حدة. يتعرف كل من منصتي iOS و أندرويد على وسم في الملف config.xml، والذي يحتوي أسماء أنظمة الملفات المراد تثبيتها. افتراضيًا، يتم تمكين كل جذور (roots) نظام الملفات.

<preference name="iosExtraFilesystems" value="library,library-nosync,documents,documents-nosync,cache,bundle,root" />
<preference name="AndroidExtraFilesystems" value="files,files-external,documents,sdcard,cache,cache-external,assets,root" />

أندرويد

  • files: مجلد التطبيق المخصص لتخزين الملفات الداخلية
  • files-external: مجلد التطبيق المخصص لتخزين الملفات الخارجية
  • sdcard: المجلد لتخزين الملفات الخارجية العامة (والذي سيكون جذر بطاقة الذاكرة SD، إن كانت مُركّبة). يجب أن يكون لديك الإذن android.permission.WRITE_EXTERNAL_STORAGE لاستخدامه.
  • cache: مجلد التخزين المؤقت الداخلي للتطبيق
  • cache-external: مجلد التخزين المؤقت الخارجي للتطبيق
  • assets: حزمة (bundle) التطبيق (للقراءة فقط)
  • root: نظام ملفات الجهاز بأكمله

يدعم أندرويد أيضًا نظام ملفات خاص باسم "documents"، والذي يمثل مجلداً فرعيًا "/Documents/" داخل نظام الملفات "files".

iOS

  • library: مجلد مكتبة التطبيق
  • documents: مجلد مستندات (Documents) التطبيق
  • cache: مجلد ذاكرة التخزين المؤقت للتطبيق
  • bundle: حزمة التطبيق، موضع التطبيق نفسه على القرص (للقراءة فقط)
  • root: نظام ملفات الجهاز بأكمله

افتراضيًا، يمكن مزامنة مجلدي المستندات (documents) والمكتبة (library) مع السحابة iCloud. يمكنك أيضًا طلب نظامي ملفات إضافيين، وهما library-nosync و documents-nosync، واللذان يمثلان مجلدًا خاصًا غير متزامن داخل نظام الملفات /Library أو /Documents.

نموذج: إنشاء ملفات والمجلدات، كتابة، قراءة، وإلحاق الملفات

تتيح لك الإضافة File تنفيذ أشياء مثل تخزين الملفات في موقع تخزين مؤقت أو دائم لأجل تطبيقك (تخزين مُحصّن [sandboxed storage])، ولتخزين الملفات في مواقع أخرى بحسب على المنصة المُستخدمة. توضح الشيفرات البرمجية في هذا القسم بعض الأمور، مثل:

  • [#persistent Accessing the file system]
  • استخدام عناوين (URL) ملفات مستقلة عن المنصات لتخزين ملفاتك [#appendFile store your files] (راجع فقرة مكان تخزين الملفات لمزيد من المعلومات)
  • إنشاء [#persistent files] و [#createDir directories]
  • [#writeFile Writing to files]
  • [#readFile Reading files]
  • [#appendFile Appending files]
  • [#displayImage Display an image file]

إنشاء ملف دائم

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

عندما تحصل على إذن الوصول إلى نظام الملفات عبر requestFileSystem، يُمنح إذن الوصول لنظام الملفات المحصّن (sandboxed) فقط (يقصُر وضع التحصين [sandbox] حق الوصول على التطبيق وحده)، ولا يمنح حق الوصول العام إلى أي موضع في نظام ملفات على الجهاز. (للوصول إلى مواضع نظام الملفات خارج المخزن المُحصّن، استخدم توابع أخرى مثل window.resolveLocalFileSystemURL، والذي يدعم المواضع المخصوصة بمنصات معينة. لمثال على ذلك، انظر فقرة إلحاق ملف.)

هنا طلبٌ للتخزين المستمر. ملاحظة: عند استهداف عملاء العارض WebView (بدلاً من المتصفح) أو التطبيقات الأصلية (ويندوز)، فلا تحتاج إلى استخدام requestQuota قبل استخدام التخزين الدائم.

window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (fs) {
    console.log('file system open: ' + fs.name);
    fs.root.getFile("newPersistentFile.txt", { create: true, exclusive: false }, function (fileEntry) {
        console.log("fileEntry is file?" + fileEntry.isFile.toString());
        // fileEntry.name == 'someFile.txt'
        // fileEntry.fullPath == '/someFile.txt'
        writeFile(fileEntry, null);
    }, onErrorCreateFile);
}, onErrorLoadFs);

تتلقى دالة النجاح (success callback) كائنًا FileSystem. استخدم fs.root لإعادة كائنٍ DirectoryEntry، والذي يمكنك استخدامه لإنشاء أو الحصول على ملف (عن طريق استدعاء getFile). في هذا المثال، fs.root هو كائنٌ DirectoryEntry يمثل التخزين الدائم في نظام الملفات المُحصّن.

تتلقى دالة النجاح (success callback) الخاصة بالتابع getFile كائناً FileEntry. يمكنك استخدام هذا لتنفيذ عمليات الكتابة والقراءة على الملف.

إنشاء ملف مؤقت

في ما يلي مثال لطلب تخزينٍ مؤقت. قد يتم حذف التخزين المؤقت من قبل نظام التشغيل في حال انحسار ذاكرة الجهاز.

window.requestFileSystem(window.TEMPORARY, 5 * 1024 * 1024, function (fs) {
    console.log('file system open: ' + fs.name);
    createFile(fs.root, "newTempFile.txt", false);
}, onErrorLoadFs);

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

function createFile(dirEntry, fileName, isAppend) {
    // Creates a new file or returns the file if it already exists.
    dirEntry.getFile(fileName, {create: true, exclusive: false}, function(fileEntry) {
        writeFile(fileEntry, null, isAppend);
    }, onErrorCreateFile);
}

الكتابة في ملف

بمجرد أن يكون لديك كائن FileEntry، يمكنك الكتابة في الملف عن طريق استدعاء createWriter، والذي يعيد كائنًا FileWriter في دالة النجاح. استدع التابع write الخاص بالكائن FileWriter للكتابة إلى الملف.

function writeFile(fileEntry, dataObj) {
    // Create a FileWriter object for our FileEntry (log.txt).
    fileEntry.createWriter(function (fileWriter) {
        fileWriter.onwriteend = function() {
            console.log("Successful file write...");
            readFile(fileEntry);
        };
        fileWriter.onerror = function (e) {
            console.log("Failed file write: " + e.toString());
        };
        // If data object is not passed in,
        // create a new Blob instead.
        if (!dataObj) {
            dataObj = new Blob(['some file data'], { type: 'text/plain' });
        }
        fileWriter.write(dataObj);
    });
}

قراءة الملف

تحتاج إلى كائن FileEntry لقراءة ملف موجود. استخدم الخاصية file في الكائن FileEntry للحصول على مرجع الملف، ثم أنشئ كائنًا جديدًا من النوع FileReader. يمكنك استخدام توابع مثل readAsText لبدء عملية القراءة. عند اكتمال عملية القراءة، سيخزن this.result نتيجة عملية القراءة.

function readFile(fileEntry) {
    fileEntry.file(function (file) {
        var reader = new FileReader();
        reader.onloadend = function() {
            console.log("Successful file read: " + this.result);
            displayFileData(fileEntry.fullPath + ": " + this.result);
        };
        reader.readAsText(file);
    }, onErrorReadFile);
}

الإضافة إلى محتوى ملف باستخدام أساليب بديلة

بالطبع، ستحتاج غالبًا إلى إضافة محتوى إلى الملفات الموجودة بدلاً من إنشاء ملفات جديدة. إليك مثالًا على ذلك. يوضح هذا المثال طريقة أخرى يمكنك من خلالها الوصول إلى نظام الملفات باستخدام window.resolveLocalFileSystemURL. في هذا المثال، قم بتمرير عنوان الملف المستقل عن المنصات، cordova.file.dataDirectory، إلى الدالة. تتلقى دالة النجاح (success callback) كائنًا DirectoryEntry، والذي يمكنك استخدامه لإنشاء ملف وغير ذلك من الأمور.

window.resolveLocalFileSystemURL(cordova.file.dataDirectory, function (dirEntry) {
    console.log('file system open: ' + dirEntry.name);
    var isAppend = true;
    createFile(dirEntry, "fileToAppend.txt", isAppend);
}, onErrorLoadFs);

بالإضافة إلى هذا الاستخدام، يمكنك استخدام التابع resolveLocalFileSystemURL للوصول إلى بعض مواضع نظام الملفات التي لا تدخل في نظام التخزين المحصّن. راجع فقرة مكان تخزين الملفات لمزيد من المعلومات؛ العديد من مواقع التخزين هذه مخصوصة بالمنصات. يمكنك أيضًا تمرير مواضع نظام ملفات مستقلة عن المنصات إلى التابع resolveLocalFileSystemURL باستخدام البروتوكول cdvfile.

بالنسبة إلى عملية الإضافة (append)، لا يوجد شيء جديد في الدالة createFile التي استدعيت في الشيفرة البرمجية السابقة (راجع الأمثلة السابقة لمطالعة الشيفرات الفعلية). الدالة createFile تستدعي writeFile. عليك أن تتحقق في writeFile مما إذا كانت عملية الإضافة مطلوبة.

بمجرد الحصول على كائن FileWriter، استدع التابع seek، ومرّر إليه فهرس الموضع الذي تريد الكتابة عنده. في هذا المثال، عليك أيضًا التحقق من وجود الملف. بعد استدعاء التابع seek، استدع التابع write الخاصة بالكائن FileWriter.

function writeFile(fileEntry, dataObj, isAppend) {
    // Create a FileWriter object for our FileEntry (log.txt).
    fileEntry.createWriter(function (fileWriter) {
        fileWriter.onwriteend = function() {
            console.log("Successful file read...");
            readFile(fileEntry);
        };
        fileWriter.onerror = function (e) {
            console.log("Failed file read: " + e.toString());
        };
        // If we are appending data to file, go to the end of the file.
        if (isAppend) {
            try {
                fileWriter.seek(fileWriter.length);
            }
            catch (e) {
                console.log("file doesn't exist!");
            }
        }
        fileWriter.write(dataObj);
    });
}

تخزين ملف ثنائي موجود

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

قبل الحصول على الملف، احصل على مرجع لكائن FileSystem باستخدام التابع requestFileSystem. ومرّر إليهwindow.TEMPORARY (كما فعلنا من قبل)، يمثل الكائن FileSystem المعاد ذاكرة التخزين المؤقت في نظام االملفات المُحصّنة. استخدم fs.root للحصول على كائن DirectoryEntry الذي تحتاجه.

window.requestFileSystem(window.TEMPORARY, 5 * 1024 * 1024, function (fs) {
    console.log('file system open: ' + fs.name);
    getSampleFile(fs.root);
}, onErrorLoadFs);

في الختام، إليك طلبية xhr للحصول على بقعة الصورة (Blob image). لا يوجد أي شيء خاص بكوردوفا في هذه الشيفرة، باستثناء أنك ستقوم إعادة توجيه مرجع DirectoryEntry الذي حصلت عليه بالفعل كوسيط للدالة saveFile. سوف تقوم بحفظ بقعة الصورة (Blob image) وعرضها في وقت لاحق بعد قراءة الملف (للتحقق من صحة العملية).

function getSampleFile(dirEntry) {
    var xhr = new XMLHttpRequest();
    xhr.open('GET', 'http://cordova.apache.org/static/img/cordova_bot.png', true);
    xhr.responseType = 'blob';
    xhr.onload = function() {
        if (this.status == 200) {
            var blob = new Blob([this.response], { type: 'image/png' });
            saveFile(dirEntry, blob, "downloadedImage.png");
        }
    };
    xhr.send();
}

ملاحظة: كجزء من إجراءات الأمان في كوردوفا 5، تتطلب الشيفرة السابقة إضافة اسم النطاق، http://cordova.apache.org، إلى العنصر Content-Security-Policy في الملف index.html.

بعد الحصول على الملف، انسخ المحتويات في ملف جديد. الكائن DirectoryEntry الحالي مرتبط بالفعل مع التخزين المؤقت للتطبيق.

function saveFile(dirEntry, fileData, fileName) {
    dirEntry.getFile(fileName, { create: true, exclusive: false }, function (fileEntry) {
        writeFile(fileEntry, fileData);
    }, onErrorCreateFile);
}

في writeFile، يمكنك تمرير في كائن الملف Blob باعتباره dataObj، وستحفظه في الملف الجديد.

function writeFile(fileEntry, dataObj, isAppend) {
    // Create a FileWriter object for our FileEntry (log.txt).
    fileEntry.createWriter(function (fileWriter) {
        fileWriter.onwriteend = function() {
            console.log("Successful file write...");
            if (dataObj.type == "image/png") {
                readBinaryFile(fileEntry);
            }
            else {
                readFile(fileEntry);
            }
        };
        fileWriter.onerror = function(e) {
            console.log("Failed file write: " + e.toString());
        };
        fileWriter.write(dataObj);
    });
}

بعد الكتابة في الملف، اقرأه وقم بعرضه. لقد حفظت الصورة كبيانات ثنائية (binary data)، حتى تتمكن من قراءتها باستخدام التابع FileReader.readAsArrayBuffer.

function readBinaryFile(fileEntry) {
    fileEntry.file(function (file) {
        var reader = new FileReader();
        reader.onloadend = function() {
            console.log("Successful file write: " + this.result);
            displayFileData(fileEntry.fullPath + ": " + this.result);
            var blob = new Blob([new Uint8Array(this.result)], { type: "image/png" });
            displayImage(blob);
        };
        reader.readAsArrayBuffer(file);
    }, onErrorReadFile);
}

بعد قراءة البيانات، يمكنك عرض الصورة باستخدام الشيفرة التالية. استخدم window.URL.createObjectURL للحصول على السلسلة النصية للشِّعب (DOM) الخاصة ببقعة الصورة (Blob image).

function displayImage(blob) {
    // Displays image if result is a valid DOM string for an image.
    var elem = document.getElementById('imageFile');
    // Note: Use window.URL.revokeObjectURL when finished with image.
    elem.src = window.URL.createObjectURL(blob);
}

عرض ملف صورة

لعرض صورة باستخدام FileEntry، يمكنك استدعاء التابع toURL.

function displayImageByFileURL(fileEntry) {
    var elem = document.getElementById('imageFile');
    elem.src = fileEntry.toURL();
}

إن كنت تستخدم عناوين مخصوصة بمنصات معينة بدلاً من FileEntry، وأردت عرض صورة، فقد تحتاج إلى تضمين الجزء الرئيسي من العنوان URI في العنصر Content-Security-Policy في الملف index.html. على سبيل المثال، في ويندوز 10، يمكنك تضمين ms-appdata: في ذلك العنصر. إليك المثال التالي:

<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: ms-appdata: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">

إنشاء المجلدات

في الشفرة التالية، ستنشئ مجلدات في جذر موضع التخزين الخاص بالتطبيق. يمكنك استخدام هذه الشيفرة مع أي موضع تخزين قابل للكتابة (DirectoryEntry). في هذا المثال، ستكتب في ذاكرة التخزين المؤقت للتطبيق (على افتراض أنك استخدمت window.TEMPORARY للحصول على الكائن FileSystem) عن طريق تمرير fs.root إلى هذه الدالة.

ننشئ هذه الشيفرة المجلد ‎/NewDirInRoot/images في ذاكرة التخزين المؤقت للتطبيق. بخصوص القيم المخصوصة بمنصات معينة، راجع فقرات بنيات نظام الملفات أعلاه.

function createDirectory(rootDirEntry) {
    rootDirEntry.getDirectory('NewDirInRoot', { create: true }, function (dirEntry) {
        dirEntry.getDirectory('images', { create: true }, function (subDirEntry) {
            createFile(subDirEntry, "fileInNewSubDir.txt");
        }, onErrorGetDir);
    }, onErrorGetDir);
}

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

مصادر