選完檔案之後,然後呢

<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);

相關文章