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