قراءة الملفات في 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);
}