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

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

توفّر واجهة برمجة التطبيقات 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);
}

الصورة الرئيسية لفنسانت بوتا من Unsplash