קריאת קבצים ב-JavaScript

בחירת קבצים ואינטראקציה איתם במכשיר המקומי של המשתמש היא אחת של האינטרנט. הוא מאפשר למשתמשים לבחור קבצים להעלות אותם לשרת. לדוגמה, כשמשתפים תמונות או שולחים נתוני מס מסמכים. הוא גם מאפשר לאתרים לקרוא ולשנות אותם בלי כדי להעביר את הנתונים ברשת. בדף הזה מוסבר איך להשתמש JavaScript לאינטראקציה עם קבצים.

ה-File System Access API המודרני

File System Access API מאפשר לקרוא קבצים ולכתוב בהם במערכת המקומית של המשתמש. האפשרות הזו זמינה ברוב המקרים שמבוססים על Chromium בדפדפנים כמו Chrome ו-Edge. מידע נוסף זמין במאמר בנושא ממשק File System Access API

מאחר ש-File System Access API לא תואם לכל הדפדפנים, אנחנו מומלץ להשתמש ב-browser-fs-access, ספרייה משנית שמשתמשת ב-API החדש בכל מקום שבו הוא זמין בחזרה לגישות הקודמות, מתי שכבר לא.

עבודה עם קבצים, בדרך הקלאסית

במדריך הזה מוסבר איך לקיים אינטראקציה עם קבצים באמצעות שיטות 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"> לרכיב מסוים. לא משנה מה יהיה אזור השחרור שלך, חשוב לוודא שהוא ברור למשתמש כדי לגרור קבצים למשטח הזה.

הגדרת אזור הירידה

כדי להפעיל רכיב כאזור גרירה ושחרור, צריך ליצור פונקציות listener עבור שני אירועים: 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);
}

התמונה הראשית (Hero) של וינסנט בוטה מתוך Unbounce