อ่านไฟล์ใน 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

หากเปิดใช้การลากและวาง ผู้ใช้อาจพยายามลากไดเรกทอรีลงในไดเรกทอรี Drop Zone เมื่อเหตุการณ์การตกหล่นเริ่มทำงาน จะมีออบเจ็กต์ 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 ข้อมูล หรือ text:

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