การเลือกและโต้ตอบกับไฟล์ในอุปกรณ์ของผู้ใช้เป็นหนึ่งในฟีเจอร์ที่ใช้กันมากที่สุดของเว็บ ซึ่งช่วยให้ผู้ใช้เลือกไฟล์และอัปโหลดไปยังเซิร์ฟเวอร์ได้ เช่น เมื่อแชร์รูปภาพหรือส่งเอกสารภาษี นอกจากนี้ยังช่วยให้เว็บไซต์อ่านและดัดแปลงข้อมูลได้โดยไม่ต้องโอนข้อมูลผ่านเครือข่าย หน้านี้จะอธิบายวิธีใช้ JavaScript เพื่อโต้ตอบกับไฟล์
File System Access API เวอร์ชันใหม่
File System Access API มีวิธีอ่านและเขียนไฟล์และไดเรกทอรีในระบบในเครื่องของผู้ใช้ ซึ่งพร้อมใช้งานในเบราว์เซอร์ที่พัฒนาบน Chromium ส่วนใหญ่ เช่น Chrome และ Edge ดูข้อมูลเพิ่มเติมเกี่ยวกับ API นี้ได้ที่ 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 ช่วยให้ผู้ใช้ลากรูปภาพไปไว้ที่ใดก็ได้ในหน้าต่าง แล้วคลิกเลือกรูปภาพเพื่อเรียกใช้องค์ประกอบ <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
Demo
อ่านเนื้อหาของไฟล์
ใช้ 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