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

الصورة الرئيسية من Vincent Botta على Unsplash