HTML Input File
使用 <input type="file" /> 取得使用者想要上傳的檔案:
- multiple 屬性可以一次上傳多個檔案
- accept 屬性可以限制上傳檔案的類型
<input type="file" id="file-uploader" data-target="file-uploader" accept="image/*" multiple="multiple"/>
限制可上傳的檔案類型 Accept Attribute
accept="image/png" accept=".png" accept="image/png, image/jpeg" accept=".png, .jpg, .jpeg" accept="image/*" accept=".doc,.docx,.xml,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document"
取得上傳檔案的基本資訊
keywords: fileInput.files
透過 e.target.files 屬性可以取得該檔案的 Blob 物件:
const fileUploader = document.querySelector('#file-uploader'); fileUploader.addEventListener('change', (e) => { console.log(e.target.files); // get file object });
❗️ e.target.files 會是一個陣列,裡面可以取得使用者所有想要上傳的檔案,陣列裡都是該檔案的 Blob 物件,而不是一般的物件。
因為這裡只有上傳一個檔案,所以使用 e.target.files[0] 即可取得使用者想要上傳的檔案。這的 File Object 是一個 Blob 物件而不是一般的物件,但從中可以透過 name, size, type 取得該檔案的資訊。
透過 AJAX 上傳檔案
FormData
keywords: FormData()
透過下面的方式,可以將欲上傳的檔案 append 到 FormData() 上:
let form = new FormData(); form.append("product[photos][]", e.target.files[i])
接著透過 fetch API 或其他方式把檔案送到後端:
// fetchAPI fetch('https://api.endpoint.io', { method: 'POST', body: form, }) // jQuery $.ajax({ processData: false, data: form, })
JSON
另一種方式是透過 JSON 來上傳檔案,步驟如下:
- 取得使用者上傳檔案:在 HTML 中建立 <input type="file" onChange={handleUpload} /> 來取得使用者上傳的檔案。
- 得到該檔案的 Blob:在 handleUpload 的 e.target.files 中可以取得該檔案的 Blob。
- 轉成 ArrayBuffer:透過 FilerReader() 來轉成 ArrayBuffer 的格式。在 reader.onLoad 的時候,可以透過 reader.result 來取得 ArrayBuffer。
- 轉成 Uint8Array:接著透過 new Uint8Array() 把這個 ArrayBuffer 轉成陣列,但要特別注意,轉出來的是「類陣列(TypedArray)」而不是真正的陣列,因此在送出 AJAX 之前需要先轉成真正的陣列。
- 轉成真正的陣列:透過 Array.from() 把剛剛的 Uint8Array 轉成真正的陣列。
- 轉成 JSON 格式:如果直接對 Uint8Array 執行 JSON.stringify() 會得到錯誤的結果,記得要先使用 Array.from() 才可以使用 JSON.stringify()
❗️透過 new Uint8Array() 轉換出來的陣列會是一個「類陣列(Typed Array)」,可以透過 Array.from() 等方式轉換成真正的陣列。
範例程式碼
JavaScript
See the Pen File Upload with JavaScript by PJCHEN (@PJCHENder) on CodePen.
JSX
// FileUploader.js import React from 'react'; async function handleUpload(e) { // STEP 2: 得到該檔案的 Blob, i.e., e.target.files const arrayBuffer = await getArrayBuffer(e.target.files[0]); console.log('arrayBuffer', arrayBuffer); const response = await uploadFile(arrayBuffer); console.log('response', response); } function getArrayBuffer(file) { return new Promise((resolve, reject) => { // STEP 3: 轉成 ArrayBuffer, i.e., reader.result const reader = new FileReader(); reader.addEventListener('load', () => { resolve(reader.result); }); reader.readAsArrayBuffer(file); }) } function uploadFile(arrayBuffer) { return fetch(`https://api.foobar.io`, { method: 'POST', // STEP 6:使用 JSON.stringify() 包起來送出 body: JSON.stringify({ appId: 3, format: 'png', // STEP 4:轉成 Uint8Array(這是 TypedArray) // STEP 5:透過 Array.from 轉成真正的陣列 icon: Array.from(new Uint8Array(arrayBuffer)), }), }).then((res)=> { if (!res.ok) { throw res.statusText; } return res.json() }) .then(({ data }) => console.log('data', data)) .catch(err => console.log('err', err)) } const FileUploader = () => { // STEP 1: 建立上傳表單 return ( <input type="file" onChange={handleUpload}/> ) } export default FileUploader;
顯示預覽圖
取得欲覽圖的方式可以透過 fileReader 或 createObjectURL:
方法一: 使用 fileReader
在 onload 中的 callback,可以透過 e.target.result 取得該檔案。
const curFile = curFiles[0]; // 透過 input 取得的 file object const reader = new FileReader(); reader.onload = function (e) { console.log('file:', e.target.result); }; // 使用 readAsDataURL 將圖片轉成 Base64 reader.readAsDataURL(curFile);
方法二:使用 createObjectURL
const curFile = curFiles[0]; // 透過 input 取得的 file object const objectURL = URL.createObjectURL(curFile); console.log('objectURL', objectURL);
常用函式
returnFileSize
function returnFileSize(number) { if (number < 1024) { return `${number}bytes`; } if (number > 1024 && number < 1048576) { return `${(number / 1024).toFixed(1)}KB`; } if (number > 1048576) { return `${(number / 1048576).toFixed(1)}MB`; } }
validFileType
function validFileType(file) { const acceptFileTypes = ["image/jpeg", "image/png"]; const isValidFileType = acceptFileTypes.includes(fileObject.type); return isValidFileType; }
表單清空
e.target.value = '';
參考資料
說明如何透過如何使用
input file, drag and drop, preview
- Input File @ MDN
- Using files from web applications @ MDN
Drag and Drop
- DataTransfer @ MDN
- HTML Drag and Drop API @ MDN
API
- File Reader @ MDN
相關閱讀
- Ant Design Upload Component @ Ant Design
- [Rails] AJAX 小技巧 @ PJCHENder HackMD
- [Rails] carrierwave uploader @ PJCHENder HackMD
圖片來源:
- jQuery File Upload Scripts