อ่านไฟล์ใน 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 แบบเดิม

เลือกไฟล์

การเลือกไฟล์มี 2 วิธีหลักๆ ได้แก่ การใช้ องค์ประกอบอินพุต HTML และการใช้ พื้นที่ลากและวาง

องค์ประกอบอินพุต HTML

วิธีที่ง่ายที่สุดสำหรับผู้ใช้ในการเลือกไฟล์คือการใช้ <input type="file"> ซึ่งเบราว์เซอร์หลักทุกเบราว์เซอร์รองรับ เมื่อคลิก องค์ประกอบนี้จะช่วยให้ผู้ใช้ เลือกไฟล์เดียวหรือหลายไฟล์ได้หากรวมแอตทริบิวต์ multiple ไว้ โดยใช้ UI การเลือกไฟล์ในตัวของระบบปฏิบัติการ เมื่อผู้ใช้เลือกไฟล์เสร็จแล้ว ระบบจะทริกเกอร์เหตุการณ์ 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>
.

ตัวอย่างต่อไปนี้ช่วยให้ผู้ใช้เลือกไฟล์หลายไฟล์ได้โดยใช้ UI การเลือกไฟล์ในตัวของระบบปฏิบัติการ แล้วบันทึกแต่ละไฟล์ที่เลือกไว้ลงในคอนโซล

จำกัดประเภทไฟล์ที่ผู้ใช้เลือกได้

ในบางกรณี คุณอาจต้องการจำกัดประเภทไฟล์ที่ผู้ใช้เลือกได้ เช่น แอปแต่งรูปภาพควรยอมรับเฉพาะรูปภาพ ไม่ใช่ไฟล์ข้อความ หากต้องการตั้งค่า ข้อจำกัดประเภทไฟล์ ให้เพิ่มแอตทริบิวต์ accept ลงในองค์ประกอบอินพุตเพื่อระบุประเภทไฟล์ที่ยอมรับ

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

การลากและวางที่กำหนดเอง

ในบางเบราว์เซอร์ องค์ประกอบ <input type="file"> ยังเป็นเป้าหมายการวางด้วย ซึ่งช่วยให้ผู้ใช้ลากและวางไฟล์ลงในแอปได้ อย่างไรก็ตาม เป้าหมายการวางนี้ มีขนาดเล็กและอาจใช้งานยาก ดังนั้น หลังจากที่คุณมีฟีเจอร์หลักโดยใช้ องค์ประกอบ <input type="file"> แล้ว คุณสามารถสร้างพื้นที่ลากและวางขนาดใหญ่ที่กำหนดเองได้

เลือกโซนการวาง

พื้นที่วางจะขึ้นอยู่กับการออกแบบแอปพลิเคชัน คุณอาจต้องการให้หน้าต่างบางส่วนเป็นพื้นที่วางเท่านั้น แต่คุณก็ใช้ทั้งหน้าต่างได้

ภาพหน้าจอของ Squoosh ซึ่งเป็นเว็บแอปบีบอัดรูปภาพ
Squoosh ทำให้ทั้งหน้าต่างเป็นพื้นที่วาง

แอปบีบอัดรูปภาพ Squoosh ช่วยให้ผู้ใช้ลากรูปภาพไปวางที่ใดก็ได้ใน หน้าต่าง และคลิก เลือกรูปภาพ เพื่อเรียกใช้องค์ประกอบ <input type="file"> ไม่ว่าคุณจะเลือกพื้นที่ใดเป็นพื้นที่วาง โปรดตรวจสอบว่าผู้ใช้เข้าใจว่าสามารถลากไฟล์ไปวางในพื้นที่นั้นได้

กำหนดพื้นที่วาง

หากต้องการเปิดใช้องค์ประกอบเป็นพื้นที่ลากและวาง ให้สร้าง Listener สำหรับ เหตุการณ์ 2 รายการ ได้แก่ dragover และ drop เหตุการณ์ dragover จะอัปเดต UI ของเบราว์เซอร์เพื่อแสดงให้เห็นว่าการลากและวางกำลังสร้างสำเนาของไฟล์ เหตุการณ์ 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

ตรวจสอบความคืบหน้าของการอ่านไฟล์

เมื่ออ่านไฟล์ขนาดใหญ่ การแสดง UX บางอย่างเพื่อบอกให้ผู้ใช้ทราบความคืบหน้าในการอ่านอาจเป็นประโยชน์ โดยให้ใช้เหตุการณ์ progress ที่ FileReader มีให้ เหตุการณ์ progress มีพร็อพเพอร์ตี้ 2 รายการ ได้แก่ 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);
}