選完檔案之後,然後呢
<input type="file"> 讓使用者選檔案,但選完之後瀏覽器只是把 File 物件放進 input.files,還沒讀內容。
<input type="file" id="upload" accept="image/*">const input = document.getElementById('upload');
input.addEventListener('change', () => {
const file = input.files[0]; // File 物件
console.log(file.name); // "photo.jpg"
console.log(file.size); // bytes
console.log(file.type); // "image/jpeg"
console.log(file.lastModified); // timestamp
});File 繼承自 Blob,拿到它之後有幾條路可以讀內容。
FileReader:非同步讀取檔案
const reader = new FileReader();
// 讀完後觸發
reader.addEventListener('load', (e) => {
console.log(e.target.result);
});
// 讀取進度(大檔案時有用)
reader.addEventListener('progress', (e) => {
if (e.lengthComputable) {
const percent = (e.loaded / e.total) * 100;
console.log(`${percent.toFixed(0)}%`);
}
});
reader.addEventListener('error', () => {
console.error('讀取失敗', reader.error);
});三種讀取方式
// 1. 讀成文字(.txt、.csv、.json)
reader.readAsText(file, 'UTF-8');
// result: "id,name\n1,Alice\n2,Bob"
// 2. 讀成 Data URL(圖片預覽用)
reader.readAsDataURL(file);
// result: "data:image/jpeg;base64,/9j/4AAQSkZJRgAB..."
// 3. 讀成 ArrayBuffer(二進位處理、Wasm 傳入)
reader.readAsArrayBuffer(file);
// result: ArrayBuffer { byteLength: 102400 }圖片預覽的完整範例:
input.addEventListener('change', () => {
const file = input.files[0];
if (!file.type.startsWith('image/')) return;
const reader = new FileReader();
reader.addEventListener('load', (e) => {
const img = document.createElement('img');
img.src = e.target.result;
document.body.appendChild(img);
});
reader.readAsDataURL(file);
});URL.createObjectURL:更快的預覽方式
大圖片用 readAsDataURL 會把整個檔案轉成 base64 字串,記憶體佔用很大。URL.createObjectURL 直接建一個指向檔案的暫時 URL,效率高很多:
input.addEventListener('change', () => {
const file = input.files[0];
// 建立暫時 URL
const objectUrl = URL.createObjectURL(file);
const img = document.createElement('img');
img.src = objectUrl;
document.body.appendChild(img);
// 用完一定要釋放,否則記憶體不會回收
img.addEventListener('load', () => {
URL.revokeObjectURL(objectUrl);
});
});原則:圖片預覽用 createObjectURL;需要把內容送去伺服器或用 JS 處理文字時才用 FileReader。
拖曳上傳
除了 input,使用者也可以直接把檔案拖進頁面:
const dropzone = document.getElementById('dropzone');
dropzone.addEventListener('dragover', (e) => {
e.preventDefault(); // 必須,否則 drop 事件不觸發
dropzone.classList.add('drag-over');
});
dropzone.addEventListener('dragleave', () => {
dropzone.classList.remove('drag-over');
});
dropzone.addEventListener('drop', (e) => {
e.preventDefault();
dropzone.classList.remove('drag-over');
const files = e.dataTransfer.files; // FileList,跟 input.files 一樣
Array.from(files).forEach(file => {
console.log(file.name, file.size);
});
});Blob:檔案以外的二進位資料
Blob 是任意二進位資料的容器,File 繼承自它。在不來自 <input> 的場合(API 回傳、Canvas 輸出),直接操作 Blob:
// Canvas 轉 Blob(截圖、圖片壓縮)
canvas.toBlob((blob) => {
const url = URL.createObjectURL(blob);
// 可以用來下載
const a = document.createElement('a');
a.href = url;
a.download = 'screenshot.png';
a.click();
URL.revokeObjectURL(url);
}, 'image/png');
// 文字轉 Blob(產生可下載的 CSV)
const csv = 'id,name\n1,Alice\n2,Bob';
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);相關文章
- 表單進階 —
input type="file"的 accept / capture 屬性 - File System Access API — 讀寫本地端檔案系統(不限於 input 選取)