前端的 File API
¶前端的 File API
這一次會將一些與檔案讀寫相關的 JavaScript 與 HTML 一起聊一聊。
而這次會用上的 HTML 都有一個共同的特性,那就是「它們本身代表內容」,但 <textarea> 除外
「HTML tag 本身代表內容」的特殊之處
無法使用偽元素。
因為偽元素是用來修飾容器中的內容的頭尾,若 HTML tag 本身就是內容,就沒有「內容的頭尾」這件事。自然也就不會有偽元素。
所以這些 HTML tag 都會自我結尾。
前端 File API
有幾個要先知道的[1]
- File & Blob: File 是一個有名字的 Blob
- FileList: 以 File 為元素的 List,但它並不是 Array
- FileReader: 用來讀取 File 的內容
- 分成同步和非同步,2 種方式
- 可將檔案轉成四種資料
- 文字
- 二進制陣列 (建議)
- 二進制字串 (不建議)
- Data URL (blob url)
- URL: 用來將 File 使用在其它的 Web 環境中 (包含 DOM tree)
¶地圖
(圖有點大,左右可滑動)
¶從 FileList 取得 File
宣告一個讓瀏覽器讀取檔案的「通道」而 DOM tree 上的 <input type="file" /> 其實是一個「檔案列表」的管理控制項
<form method="post" action="upload" name="myForm" enctype="multipart/form-data">
<input type="file" name="myUploadFile" />
</form>
在 <form> 使用 name 屬性,像是宣告一樣的直接在 document 裡產生 property
const inputFile = document.myForm.myUploadFile; // <input type="file" name="myUploadFile" />
inputFile.files; // File List
inputFile.files.item(0); // File of File List
¶從 File 下載
利用 <a href="" /> 將檔案轉成 blob url 放到 href 並且點擊,就可以完成下載 blob 檔案的功能。
<button id="downloadFileButton" type="button" name="button">download File</button>
const downloadFileButton = document.querySelector("#downloadFileButton");
downloadFileButton.addEventListener("click", e => {
const anchor = document.createElement("a");
anchor.href = uploadFile.getBlobUrl();
// anchor.target = '_blank'; // Chrome 測試,非必要
anchor.download = "new_" + uploadFile.filename();
anchor.click();
});
- 取得檔案的 blob url
- 設定
<a href="" />的href設定成 blob url - 觸發 click 事件。 (如果已使用 a 的 click 進行這一連串的動作,就不用再觸發一次 click)
¶File to Image
在想要做上傳圖片的預覽時,就需要將 <input type="file" /> 的內容,放到 <img src="" /> 裡。
那實際上是怎麼做到的呢?
<img id="image" src="" alt="" />
const inputFile = document.myForm.myUploadFile; // 要先有 <input type=file />
const img = document.querySelector("#image");
const oneFile = inputFile.files.item(0);
blob_url = URL.createObjectURL(oneFile);
img.src = blob_url;
- 取出 File
- 產生 Blob url
- img.src = Blob url
¶Image to Canvas
image 到 canvas 算是 canvas 的特別之處,不算是 File API 的內容。 (不過還是放上來了)
繪製到 canvas 之後,會改變 image 的大小。
<canvas id="canvas" width="200" height="10" />
const img = document.querySelector("#image"); // 要先有 <image />
const canvas = document.querySelector("#canvas");
(canvas.width = img.width), (canvas.height = img.height);
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, img.width, img.height); // 重新繪製大小
- 取得 image object (也可以用
new Imgae()) - 將 image object 丟進 canvas 的
context.drawImage()第一個參數,就顯示畫面
¶Canvas as Base64
canvas.toDataURL() 的輸出是一個至少還有 “data:” 的 DataURL[2]
DataURL 依照 MIME type 判斷輸出方式
如果是文字,就會輸出文字,不然就會給 Base64[3]
<textarea id="base64" name="name" rows="8" cols="80"></textarea>
const canvas = document.querySelector("#canvas"); // 要先有 <canvas />
// const ctx = canvas.getContext('2d');
// ctx.drawImage(img, 0, 0, img.width, img.height);
const base64 = document.querySelector("#base64");
base64.value = canvas.toDataURL("image/jpeg");
¶File to Base64
上述得到 Base64 的方式是要透過 File -> Blob URL -> <img> -> <canvas> -> Base64
可以直接得到 Base64 嗎?
¶File to text
直接從 File 取得「文字檔案」的內容,要使用 FileReader 物件的 readAsText 方法,並且 binding 事件,在讀取結束時,取得檔案內容 (還可以取得其它的資訊)。
<textarea id="text" name="name" rows="8" cols="80"></textarea>
const text = document.querySelector("#text");
const reader = new FileReader();
reader.onload = function(e) {
text.innerText = e.target.result;
};
const inputFile = document.myForm.myUploadFile;
const oneFile = inputFile.files.item(0);
blobUrl = URL.createObjectURL(oneFile);
reader.readAsText(blobUrl, "UTF-8");
- 宣告 FileReader 物件
- 設定 onload 事件,用
e.target.result取得內容 - 取得 File 物件的 Blob url
- 將 Blob url 放進 FileReader 物件的
readAsTextmethod
¶File to binary
直接從 File 取得「圖形檔案」的內容,要使用 FileReader 物件的 readAsArrayBuffer 方法,並且 binding 事件,在讀取結束時,取得檔案內容。
<textarea id="binaryString" name="name" rows="8" cols="80"></textarea>
const binaryString = document.querySelector("#binaryString");
const reader = new FileReader();
reader.onload = function(e) {
binaryString.innerText = JSON.stringify(
new Uint8Array(e.target.result).join(", ")
);
};
const inputFile = document.myForm.myUploadFile;
const oneFile = inputFile.files.item(0);
blobUrl = URL.createObjectURL(oneFile);
reader.readAsArrayBuffer(blobUrl);
- 宣告 FileReader 物件
- 設定 onload 事件,用
e.target.result取得內容 - 圖形檔案的資料是 0~255 (8 bit 的大小),所以用
new Uint8Array將內容丟進去建構。[4] - 取得 File 物件的 Blob url
- 將 Blob url 放進 FileReader 物件的
readAsArrayBuffermethod
¶最後
程式碼會同步在 github 更新[5]