อ่านไฟล์ใน 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 event updates the browser UI to visually indicate that the drag-and-drop action is creating a copy of the file. 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);
}

รูปภาพหลักโดย Vincent Botta จาก Unsplash