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