前言
在 HTML5 File API 出現(xiàn)之前,前端對于文件的操作是非常有局限性的辅鲸,大多需要配合后端實現(xiàn)格郁。出于安全角度考慮,從本地上傳文件時独悴,代碼不可能獲取文件在用戶本地的地址例书,所以純前端不可能完成一些類似圖片預(yù)覽的功能。但是 File API 的出現(xiàn)刻炒,讓這一切變成了可能决采。
跟著樓主由淺入深,了解下強大的 File API 吧坟奥。
FileList
FileList 對象針對表單的 file 控件树瞭。當(dāng)用戶通過 file 控件選取文件后,這個控件的 files 屬性值就是 FileList 對象爱谁。它在結(jié)構(gòu)上類似于數(shù)組晒喷,包含用戶選取的多個文件。如果 file 控件沒有設(shè)置 multiple 屬性访敌,那么用戶只能選擇一個文件凉敲,F(xiàn)ileList 對象也就只有一個元素了。
<input type='file' multiple />
<script>
document.querySelector('input').onchange = function() {
console.log(this.files);
};
</script>
比如我選擇了兩個文件寺旺,控制臺打右ァ:
FileList {0: File, 1: File, length: 2}
0: File
1: File
length:2
__proto__: Object
除了用 file 控件,采用拖放方式阻塑,也可以得到 FileList 對象蓝撇。關(guān)于拖放,可以參考下我以前寫的文章 HTML5 — 讓拖放變的流行起來叮姑。
<textarea></textarea>
<script>
var ipt = document.querySelector('textarea');
ipt.ondragover = function () { return false; };
// Add drop handler
ipt.ondrop = function(e) {
e.stopPropagation();
e.preventDefault();
e = e || window.event;
var files = e.dataTransfer.files;
console.log(files);
};
</script>
選中兩個文件拖放到文本框中唉地,打印結(jié)果和上面一致。
一般來說传透,我們不可能手動構(gòu)造 FileList 對象耘沼,只能被動地讀取,也就是說只有用戶主動觸發(fā)了文件讀取行為朱盐,js 才能訪問到 FileList群嗤,而這通常發(fā)生在表單選擇文件或者拖拽文件中。
File
我們看到一個 FileList 對象包含了我們選中的 File 對象兵琳,那么一個 File 又有哪些屬性呢狂秘?我們可以打印出來看看。
- name:文件名躯肌,該屬性只讀者春。
- size:文件大小,單位為字節(jié)清女,該屬性只讀钱烟。
- type:文件的 MIME 類型,如果分辨不出類型嫡丙,則為空字符串拴袭,該屬性只讀。
- lastModified:文件的上次修改時間曙博,格式為時間戳拥刻。
- lastModifiedDate:文件的上次修改時間,格式為 Date 對象實例父泳。
Blob
上圖中我們看到般哼,F(xiàn)ile 對象是繼承自 Blob 對象的,Blob 又是什么鬼尘吗?
Blob(Binary Large Object)對象代表了一段二進(jìn)制數(shù)據(jù)逝她,提供了一系列操作接口。其他操作二進(jìn)制數(shù)據(jù)的 API(比如 File 對象)睬捶,都是建立在 Blob 對象基礎(chǔ)上的黔宛,繼承了它的屬性和方法臀晃。
生成 Blob 對象有兩種方法:一種是使用 Blob 構(gòu)造函數(shù)座韵,另一種是對現(xiàn)有的 Blob 對象使用 slice 方法切出一部分瓣距。
(1)Blob 構(gòu)造函數(shù)呐芥,接受兩個參數(shù)。第一個參數(shù)是一個包含實際數(shù)據(jù)的數(shù)組,第二個參數(shù)是數(shù)據(jù)的類型,這兩個參數(shù)都不是必需的。
var a = ["hello", "world"];
var myBlob = new Blob(a, { "type" : "text/xml" });
console.log(myBlob);
(2)Blob 對象的 slice 方法谷徙,將二進(jìn)制數(shù)據(jù)按照字節(jié)分塊屈尼,返回一個新的 Blob 對象鞭执。
var a = ["hello", "world"];
var myBlob = new Blob(a, { "type" : "text/xml" });
var newBlob = myBlob.slice(0, 5);
console.log(newBlob);
Blob 對象有兩個只讀屬性:
- size:二進(jìn)制數(shù)據(jù)的大小芒粹,單位為字節(jié)。(文件上傳時可以在前端判斷文件大小是否合適)
- type:二進(jìn)制數(shù)據(jù)的 MIME 類型大溜,全部為小寫是辕,如果類型未知,則該值為空字符串猎提。(文件上傳時可以在前端判斷文件類型是否合適)
FileReader
重頭戲來了,F(xiàn)ileReader API 才是我們接下去完成一些任務(wù)的關(guān)鍵旁蔼。FileReader API 用于讀取文件锨苏,即把文件內(nèi)容讀入內(nèi)存。它的參數(shù)是 File 對象或 Blob 對象棺聊。
對于不同類型的文件伞租,F(xiàn)ileReader 提供不同的方法讀取文件。
readAsBinaryString(Blob|File):返回二進(jìn)制字符串限佩,該字符串每個字節(jié)包含一個 0 到 255 之間的整數(shù)葵诈。(已廢棄)- readAsText(Blob|File, opt_encoding):返回文本字符串。默認(rèn)情況下祟同,文本編碼格式是 UTF-8作喘,可以通過可選的格式參數(shù),指定其他編碼格式的文本晕城。
- readAsDataURL(Blob|File):返回一個基于 Base64 編碼的 data-uri 對象泞坦。
- readAsArrayBuffer(Blob|File):返回一個 ArrayBuffer 對象。
除了以上四種不同的讀取文件方法砖顷,F(xiàn)ileReader API 還有一個 abort 方法贰锁,用于中止文件上傳。
var reader = new FileReader();
reader.abort();
FileReader 對象采用異步方式讀取文件滤蝠,可以為一系列事件指定回調(diào)函數(shù)豌熄。
- onabort 方法:讀取中斷或調(diào)用 reader.abort() 方法時觸發(fā)。
- onerror 方法:讀取出錯時觸發(fā)物咳。
- onload 方法:讀取成功后觸發(fā)锣险。
- onloadend 方法:讀取完成后觸發(fā),不管是否成功览闰。觸發(fā)順序排在 onload 或 onerror 后面囱持。
- onloadstart 方法:讀取將要開始時觸發(fā)。
- onprogress 方法:讀取過程中周期性觸發(fā)焕济。(可以用來獲取文件讀取的進(jìn)度)
以前在學(xué)習(xí)圖片的 base64 編碼的時候纷妆,寫了一篇文章 獲取圖片 base64 編碼的幾種方法,當(dāng)時還沒有學(xué)習(xí) File API晴弃,了解到 File API 也能做類似的事情掩幢,現(xiàn)在學(xué)到了逊拍,寫了個簡單的 demo http://hanzichi.github.io/2016/image2base64/,不僅能獲取圖片的 base64 編碼际邻,同時也能獲取文字的 base64 編碼芯丧,代碼比較簡單就不放了,可以 猛戳這里世曾。
獲取到了文件的 base64 編碼缨恒,做一些諸如圖片預(yù)覽的功能,也就手到擒來了轮听,有興趣的可以自己嘗試下骗露,類似的還有文字預(yù)覽啊,等等血巍。
URL
你以為 File API 就這樣了嗎萧锉?非也,還有個強大的東西沒有介紹述寡,URL 對象柿隙!
調(diào)用 URL 對象的 createObjectURL 方法,傳入一個 File 對象或者 Blob 對象鲫凶,能生成一個鏈接禀崖,聽起來好像很吊的樣子。
var objecturl = window.URL.createObjectURL(blob);
上面的代碼會對二進(jìn)制數(shù)據(jù)生成一個 URL螟炫,這個 URL 可以放置于任何通撤溃可以放置 URL 的地方,比如 img 標(biāo)簽的 src 屬性不恭。需要注意的是叶雹,即使是同樣的二進(jìn)制數(shù)據(jù),每調(diào)用一次 URL.createObjectURL 方法换吧,就會得到一個不一樣的 URL折晦。
這個 URL 的存在時間,等同于網(wǎng)頁的存在時間沾瓦,一旦網(wǎng)頁刷新或卸載满着,這個 URL 就失效。(File 和 Blob 又何嘗不是這樣呢)除此之外贯莺,也可以手動調(diào)用 URL.revokeObjectURL 方法风喇,使 URL 失效。
window.URL.revokeObjectURL(objectURL);
舉個簡單的例子缕探。
var blob = new Blob(["Hello hanzichi"]);
var a = document.createElement("a");
a.href = window.URL.createObjectURL(blob);
a.download = "a.txt";
a.textContent = "Download";
document.body.appendChild(a);
頁面上生成了一個超鏈接魂莫,點擊它就能下載一個名為 a.txt
的文件,里面的內(nèi)容是 Hello hanzichi
爹耗。
這里插點題外話耙考,簡單介紹下 H5 新增的 download 屬性谜喊。對于一些諸如 exe,rar 等瀏覽器不能直接打開的文件類型倦始,我們一般可以直接用一個 a 標(biāo)簽斗遏,將其指向文件在服務(wù)端的地址,點擊即可下載鞋邑。但是如果是一些瀏覽器能直接打開的文件诵次,比如 txt,js 等枚碗,如果這樣設(shè)置一個超鏈接逾一,點擊會直接打開文件,一般我們可以配合后端實現(xiàn)视译,比如用 PHP。
$file_name = "1.txt"; // 下載文件名
$file_dir = dirname(__FILE__). '/'; //下載文件存放目錄
//輸入文件標(biāo)簽
Header("Content-type: text/plain");
Header("Content-Disposition: attachment; filename=" . $file_name );
以上代碼需要文件的 Content-type 屬性值归敬,安利一個網(wǎng)址酷含,http://tool.oschina.net/commons ,各種文件類型的 Content-type 屬性值一網(wǎng)打盡汪茧!
如果考慮到安全性椅亚,header + fread 可能會顯得更嚴(yán)謹(jǐn)。
$file_name = "1.txt"; // 下載文件名
$file_dir = dirname(__FILE__). '/'; //下載文件存放目錄
Header("Content-type: text/plain");
Header("Content-Disposition: attachment; filename=" . $file_name );
echo fread($file, filesize($file_dir . $file_name));
但是現(xiàn)在我們只需要在 a 標(biāo)簽上加上 download 舱污!
<a href="1.txt" download>download txt></a>
還可以給 download 加上屬性值呀舔,即為下載的文件名。
<a href="1.txt" download="2.txt">download txt></a>
可以省略.txt
的后綴名扩灯,瀏覽器會自行判斷媚赖。
我們再回到 URL 上來。對于 File 或者 Blob 對象珠插,我們可以這樣理解惧磺,它們的存在,依賴于頁面捻撑,而 URL 能給這些 "轉(zhuǎn)瞬即逝" 的二進(jìn)制對象一個臨時的指向地址磨隘。
這個臨時的地址還有什么用呢?也能做圖片預(yù)覽顾患,相比前面用 readAsDataURL 的實現(xiàn)番捂,更簡單了。
<input type='file' multiple /><br/>
<img />
<script>
document.querySelector("input").onchange = function() {
var files = this.files;
document.querySelector("img").src = window.URL.createObjectURL(files[0]);
}
</script>
比如還有這樣的需求江解,前端上傳文件设预,要動態(tài)生成該文件的下載鏈接,也能用 URL 完成犁河。
Canvas & dataURL & Blob
canvas 中有 toDataURL 函數(shù)絮缅,可以將 canvas 轉(zhuǎn)為 dataURL 形式的 base64 編碼鲁沥,而 Blob 也可以轉(zhuǎn)為 dataURL,這三者之間是否可以互相轉(zhuǎn)換耕魄?有沒有什么實用之處画恰?
(1)canvas -> dataURL
用 toDataURL 方法,比較簡單吸奴,不多說允扇。
(2)blob -> dataURL
用 FileReader 的 readAsDataURL 方法,使用方式可以看 這個 demo
(3)dataURL -> blob
這個函數(shù)有點屌
function dataURLtoBlob(dataurl) {
var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while(n--){
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {type:mime});
}
(4)dataURL - canvas
將 image 的 src 屬性置為 dataURL则奥,再用 drawImage 方法畫上去考润。
(5)blob - canvas
如何把二進(jìn)制形式的圖片畫上 canvas?先用 readAsDataURL 轉(zhuǎn)為 dataURL读处,接著就是 (4) 的事情了糊治。
(6)canvas - blob
canvas 轉(zhuǎn)為 blob 也可以用 dataURL 做跳板,先將 canvas 轉(zhuǎn)為 dataURL(1)罚舱,再用 dataURL 轉(zhuǎn)為 blob(3)井辜。
利用它們之間的轉(zhuǎn)換可以做些什么好玩的事呢?比如可以上傳圖片管闷,對圖片做各種處理粥脚,然后保存,看起來好像挺好玩的包个,等有空了搞個 demo 出來刷允。
2016.11.11 add: canvas 有原生的 toBlob 方法,使得圖片文件可以被緩存或保存到本地碧囊。
Read more
File API 還能實現(xiàn)很多炫酷的功能树灶,比如查看文件讀取的進(jìn)度(onprogress ),分割文件糯而,分段讀取文件破托,還能配合 ajax 使用,有興趣的可以參考以下文獻(xiàn)自行研究歧蒋。
- 文件和二進(jìn)制數(shù)據(jù)的操作(本文大量參考或者直接引用了阮老師的這篇文章內(nèi)容土砂,不得不說阮老師的 tutorial 寫的真的非常的好,老少皆宜)
- 通過 File API 使用 JavaScript 讀取文件
- Using files from web applications
- HTML5 讀取 本地文件 File Api 使用
- FileAPI w3
- 了解 HTML/HTML5 中的 download 屬性
- 在瀏覽器端用 JS 創(chuàng)建和下載文件
- DataURL 與 File,Blob,canvas 對象之間的互相轉(zhuǎn)換的 Javascript
轉(zhuǎn)自https://www.cnblogs.com/zichi/p/html5-file-api.html#commentform