這篇文章的起因是想到過去寫的 JS Demo 多數沒有保留,
好不容易,上次在學校弄人工智慧有保留一份帶有檔案處理的。
所以這篇文章整理了過去做過、
未來也可能用在本網誌的演示部份而寫。
歷史
沒記錯的話,早期的瀏覽器是不太允許讀取本機資料的。
理由是,如果瀏覽器的 JavaScript 可以隨意讀取本機資料,
那可能看一看網站,所有東西都偷偷被上傳了。
然而,這造成了一個問題:
「每次要檔案處理,都要透過檔案上傳到伺服器,然後再下載回來」
這個模式的問題在於網路頻寬的浪費,
也有可能網路的傳輸時間遠大於檔案處理的時間。
HTML5 標準中出現了 File API 處理了這個問題。
部份瀏覽器不允許第三方存取檔案,本文以 Google Chrome 瀏覽器為主。
讀取檔案
下面是直接從本機拿資料給 JavaScript 的做法:
HTML 必須的有 input 標籤,
至於按鈕只是為了給使用者按的(不然設定在 input.onchange 也是可以)
1 2 3 4
| ... <p>基本的上傳檔案</p> <input type="file" /> <button>上傳</button> ...
|
JavaScript 的部份,最主要就是那個 FileReader 的物件:
1 2 3 4 5 6 7 8 9 10 11 12 13
| ... buttonNode.onclick = function() { let file = inputNode.files[0]; let fileReader = new FileReader();
fileReader.onload = function() { let contents = event.target.result; ... }; fileReader.readAsDataURL(file); }; ...
|
演示
這個演示從本機端直接讀取檔案,然後顯示出檔案內容(文字):
本演示的 input(file) 標籤做了樣式處理,可供參考。
讀取進度
雖然從本機端直接讀取不用透過網路已經很快了,
但有時候檔案還是大的多,這個時候我們可能會想知道讀取的進度。
這時,我們的進度可以從 FileReader 的 onprogress 取得(可以搭配 progress 標籤使用)
1 2 3
| ... <progress value="0" max="100"></progress> ...
|
JavaScript 的部份:
1 2 3 4 5 6 7
| ...
fileReader.onprogress = function() { let rate = event.loaded / event.total; ... }; ...
|
演示
這個演示製作了進度條(需要大一點的檔案才能看到)
本演示的 progress 標籤做了樣式處理,可供參考。
讀取圖像
畫布(Canvas)是 HTML5 幾乎最核心的功能之一,
當然讀取圖像繪畫到畫布上,也是很常見的。
使用 Image 物件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| ...
let file = inputNode.files[0];
let img = new Image();
img.onload = function() { context.drawImage(img, 0, 0); };
img.src = URL.createObjectURL(file); ...
|
演示
這個演示示範了基本從 Image 到 Canvas 的過程:
使用 FileReader 物件
通常而言會推薦使用 Image 物件,不過透過 FileReader 也可實作。
如果是圖像處理建議使用 Image 物件;
這是因為 FileReader 的瀏覽器支援度不如 Image 物件普及。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| ...
let file = inputNode.files[0]; let fileReader = new FileReader();
fileReader.onload = function() { let content = event.target.result; let img = new Image(); img.onload = function() { context.drawImage(img, 0, 0); }; img.src = contents; };
fileReader.readAsDataURL(file); ...
|
演示
如果透過 FileReader 一樣可以畫在畫布上:
二進位檔案處理
取得了檔案通常需要做處理(透過 JavaScript)尤其是二進位的檔案,
轉檔、壓縮之類的往往需要二進位的控制,然而這個已經被 JavaScript 支援了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| ...
DataView.prototype.getString = function(offset, len) { let result = ""; for(let i = 0; i < len; i++) { let value = this.getUint8(offset + i); result += String.fromCharCode(value); } return result; }; ...
let file = inputNode.files[0]; let fileReader = new FileReader();
fileReader.onload = function() { let contents = new DataView(event.target.result); ... };
fileReader.readAsArrayBuffer(file); ...
|
演示
這個演示需要 WAV 檔案,可跳至 下載檔案演示 的部分;
該演示將由二進制組合一個 WAV 檔案下載。
它解析檔案內容,讀取資料(首個聲道的內容)繪畫到畫布上:
下載檔案
我們討論了很多的讀取、操作檔案的方法,
同樣的,我們也要讓使用者能把檔案從瀏覽器上拿回來才行。
具體的作法是,透過一個連結標籤。
然後設定相關參數,並執行自身(讓瀏覽器自動處理)下載:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| ... let buffer = new ArrayBuffer(...); let contents = new DataView(buffer);
... ...
let blob = new Blob([buffer], {type: "application/octet-stream"});
let url = window.URL.createObjectURL(blob); let downloadNode = document.createElement("a");
downloadNode.style.display = "none"; downloadNode.href = url; downloadNode.download = "...";
document.body.appendChild(downloadNode); downloadNode.click();
URL.revokeObjectURL(url); ...
|
演示
這個演示會根據你的函數內容生成、下載 WAV 檔案:
對於 16-bit 的 WAV 文件,範圍在 [-32768, 32767] 的區間內。
參考資料