قراءة الملفات في JavaScript

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

واجهة برمجة التطبيقات الحديثة File System Access API

توفّر واجهة برمجة التطبيقات File System Access API طريقة للقراءة من الملفات والأدلة والكتابة إليها على النظام المحلي للمستخدم. وتتوفر هذه الميزة في معظم المتصفحات المستندة إلى Chromium مثل Chrome وEdge. للمزيد من المعلومات، يمكنك مراجعة مقالة File System Access API.

نظرًا لعدم توافق واجهة برمجة التطبيقات File System Access API مع جميع المتصفّحات، ننصحك باستخدام browser-fs-access، وهي مكتبة مساعِدة تستخدم واجهة برمجة التطبيقات الجديدة حيثما كانت متاحة وتعود إلى الأساليب القديمة في حال عدم توفّرها.

العمل على الملفات بالطريقة الكلاسيكية

يوضِّح لك هذا الدليل كيفية التفاعل مع الملفات باستخدام طرق JavaScript القديمة.

اختيار ملفات

هناك طريقتان أساسيتان لاختيار الملفات: استخدام عنصر إدخال HTML، واستخدام منطقة السحب والإفلات.

عنصر إدخال HTML

إنّ أسهل طريقة للمستخدمين لاختيار الملفات هي استخدام العنصر <input type="file"> المتوافق في كل متصفّحات رئيسية. عند النقر على الزر، يتم السماح للمستخدم باختيار ملف أو عدة ملفات إذا تم تضمين السمة multiple، وذلك باستخدام واجهة المستخدم المدمجة في نظام التشغيل لاختيار الملفات. عندما ينتهي المستخدِم من اختيار ملف أو ملفات، يتم تنشيط حدث change للعنصر. يمكنك الوصول إلى قائمة الملفات من event.target.files، وهي كائن FileList. كل عنصر في السمة FileList هو كائن File.

<!-- The `multiple` attribute lets users select multiple files. -->
<input type="file" id="file-selector" multiple>
<script>
  const fileSelector = document.getElementById('file-selector');
  fileSelector.addEventListener('change', (event) => {
    const fileList = event.target.files;
    console.log(fileList);
  });
</script>

يتيح المثال التالي للمستخدم تحديد عدة ملفات باستخدام واجهة المستخدم المضمنة في اختيار الملفات في نظام التشغيل الذي يستخدمه ثم تسجيل كل ملف محدد في وحدة التحكم.

تقييد أنواع الملفات التي يمكن للمستخدمين اختيارها

في بعض الحالات، قد تحتاج إلى تحديد أنواع الملفات التي يمكن للمستخدمين اختيارها. على سبيل المثال، يجب أن يقبل تطبيق تعديل الصور الصور فقط، وليس الملفات النصية. لضبط قيود نوع الملف، أضِف السمة accept إلى عنصر الإدخال لتحديد أنواع الملفات المقبولة:

<input type="file" id="file-selector" accept=".jpg, .jpeg, .png">

السحب والإفلات المخصّص

في بعض المتصفّحات، يكون العنصر <input type="file"> أيضًا هدف إفلات، ما يتيح للمستخدمين سحب الملفات وإفلاتها في التطبيق، إلا أنّ هدف الإفلات هذا صغير وقد يصعُب استخدامه. بدلاً من ذلك، بعد توفير الميزات الأساسية باستخدام عنصر <input type="file">، يمكنك توفير سطح كبير ومخصّص للسحب والإفلات.

اختيار منطقة التحميل

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

لقطة شاشة لتطبيق Squoosh، وهو تطبيق ويب لضغط الصور
يؤدي استخدام Squoosh إلى تحويل النافذة بأكملها إلى منطقة إفلات.

يتيح تطبيق ضغط الصور Squoosh للمستخدم سحب صورة إلى أي مكان داخل النافذة، والنقر على اختيار صورة لاستدعاء عنصر <input type="file">. مهما كان اختيارك منطقة الإفلات، تأكد من أن المستخدم واضحًا أنه يمكنه سحب الملفات إلى ذلك السطح.

تحديد منطقة الإفلات

لتفعيل عنصر كمنطقة سحب وإفلات، يمكنك إنشاء أدوات معالجة لحدثَين: dragover وdrop. يحدّث الحدث dragover واجهة مستخدم المتصفّح للإشارة بشكل مرئي إلى أنّ إجراء السحب والإفلات ينشئ نسخة من الملف. يتم تنشيط حدث drop بعد أن يسقط المستخدم الملفات على السطح. كما هو الحال مع عنصر الإدخال، يمكنك الوصول إلى قائمة الملفات من event.dataTransfer.files، وهي كائن FileList. كل عنصر في FileList هو كائن File.

const dropArea = document.getElementById('drop-area');

dropArea.addEventListener('dragover', (event) => {
  event.stopPropagation();
  event.preventDefault();
  // Style the drag-and-drop as a "copy file" operation.
  event.dataTransfer.dropEffect = 'copy';
});

dropArea.addEventListener('drop', (event) => {
  event.stopPropagation();
  event.preventDefault();
  const fileList = event.dataTransfer.files;
  console.log(fileList);
});

event.stopPropagation() وevent.preventDefault() يؤديان إلى إيقاف السلوك التلقائي للمتصفّح والسماح بتشغيل الرمز بدلاً من ذلك. وبدونها، كان سينتقل المتصفح بعيدًا عن صفحتك ويفتح الملفات التي أسقطها المستخدم في نافذة المتصفح.

لمشاهدة عرض توضيحي مباشر، يُرجى الرجوع إلى السحب والإفلات المخصّص.

ماذا عن الأدلة؟

ليس هناك طريقة جيدة للوصول إلى دليل باستخدام JavaScript.

تتيح السمة webkitdirectory على العنصر <input type="file"> للمستخدم اختيار دليل أو أدلة. وهو متوافق في معظم المتصفحات الرئيسية باستثناء Firefox لنظام التشغيل Android وSafari على iOS.

إذا تم تفعيل السحب والإفلات، فقد يحاول المستخدم سحب دليل إلى منطقة الإفلات. عند تنشيط حدث الإفلات، يتضمّن كائن File للدليل، ولكنه لا يتيح الوصول إلى أي من الملفات في الدليل.

قراءة البيانات الوصفية للملف

يحتوي الكائن File على بيانات وصفية عن الملف. توفر معظم المتصفحات اسم الملف وحجم الملف ونوع MIME، على الرغم من أنه بناءً على النظام الأساسي، قد توفر المتصفحات المختلفة معلومات مختلفة أو إضافية.

function getMetadataForFileList(fileList) {
  for (const file of fileList) {
    // Not supported in Safari for iOS.
    const name = file.name ? file.name : 'NOT SUPPORTED';
    // Not supported in Firefox for Android or Opera for Android.
    const type = file.type ? file.type : 'NOT SUPPORTED';
    // Unknown cross-browser support.
    const size = file.size ? file.size : 'NOT SUPPORTED';
    console.log({file, name, type, size});
  }
}

يمكنك الاطّلاع على هذا الإجراء عمليًا في عرض input-type-file.

قراءة محتوى ملف

يمكنك استخدام FileReader لقراءة محتوى عنصر File في الذاكرة. يمكنك الطلب من FileReader قراءة ملف بصفته مخزنًا مؤقتًا للصفيف أو عنوان URL للبيانات أو نص:

function readImage(file) {
  // Check if the file is an image.
  if (file.type && !file.type.startsWith('image/')) {
    console.log('File is not an image.', file.type, file);
    return;
  }

  const reader = new FileReader();
  reader.addEventListener('load', (event) => {
    img.src = event.target.result;
  });
  reader.readAsDataURL(file);
}

يقرأ هذا المثال File الذي أدخله المستخدم، ثم يحوّله إلى عنوان URL للبيانات ويستخدم عنوان URL للبيانات هذا لعرض الصورة في عنصر img. لمعرفة كيفية التحقّق من أنّ المستخدم قد اختار ملف صورة، يمكنك الاطّلاع على العرض التوضيحي read-image-file.

مراقبة تقدم قراءة ملف

عند قراءة الملفات الكبيرة، قد يكون من المفيد توفير بعض تجربة المستخدم لإخبار المستخدم بمدى تقدم القراءة. ولإجراء ذلك، استخدِم حدث progress المتوفّر من خلال FileReader. يحتوي حدث progress على سمتَين: loaded (المقدار الذي تتم قراءته) وtotal (المقدار المطلوب قراءته).

function readFile(file) {
  const reader = new FileReader();
  reader.addEventListener('load', (event) => {
    const result = event.target.result;
    // Do something with result
  });

  reader.addEventListener('progress', (event) => {
    if (event.loaded && event.total) {
      const percent = (event.loaded / event.total) * 100;
      console.log(`Progress: ${Math.round(percent)}`);
    }
  });
  reader.readAsDataURL(file);
}

صورة رئيسية من تصوير "فنسنت بوتا" من موقع Unسبلاش