JavaScript中的FileReader示例詳解
前言:
FileReader是一種異步文件讀取機(jī)制,結(jié)合input:file可以很方便的讀取本地文件。
input:file
在介紹FileReader之前,先簡(jiǎn)單介紹input的file類型。
<input type="file" id="file">
input的file類型會(huì)渲染為一個(gè)按鈕和一段文字。點(diǎn)擊按鈕可打開(kāi)文件選擇窗口,文字表示對(duì)文件的描述(大部分情況下為文件名);file類型的input會(huì)有files屬性,保存著文件的相關(guān)信息。
document.querySelector('#file').addEventListener('change',(fileChoosed) => { console.log('fileChoosed', fileChoosed.target.files) })
點(diǎn)擊按鈕上傳一個(gè)文件后,在控制臺(tái)打印上傳的文件信息,如下:
可以發(fā)現(xiàn)文件信息是一個(gè)json對(duì)象,由傳入的file對(duì)象組成。每個(gè)file對(duì)象(即每個(gè)上傳文件)包含以下屬性:
lastModified:數(shù)值,表示最近一次修改時(shí)間的毫秒數(shù);
lastModifiedDate:對(duì)象,表示最近一次修改時(shí)間的Date對(duì)象;
name:本地文件系統(tǒng)中的文件名;
size:文件的字節(jié)大?。?/p>
type:字符串,文件的MIME類型;
webkitRelativePath:此處為空;當(dāng)在input上加上webkitdirectory屬性時(shí),用戶可選擇文件夾,此時(shí)webkitRelativePath表示文件夾中文件的相對(duì)路徑。
<input type="file" id="file" webkitdirectory>
FileReader
以上file對(duì)象只獲取到了對(duì)文件的描述信息,但沒(méi)有獲得文件中的數(shù)據(jù)。我們可以通過(guò)html5提供的FileReader讀取到文件中的數(shù)據(jù)。
首先創(chuàng)建一個(gè)FileReader實(shí)例:
const reader = new FileReader();
FileReader提供了如下方法:
readAsArrayBuffer(file)
按字節(jié)讀取文件內(nèi)容,結(jié)果用ArrayBuffer對(duì)象表示
readAsBinaryString(file)
按字節(jié)讀取文件內(nèi)容,結(jié)果為文件的二進(jìn)制串
readAsDataURL(file)
讀取文件內(nèi)容,結(jié)果用data:url的字符串形式表示
readAsText(file,encoding)
按字符讀取文件內(nèi)容,結(jié)果用字符串形式表示
abort()
終止文件讀取操作
readAsDataURL和readAsText較為常用,這里只對(duì)這兩者進(jìn)行說(shuō)明。
readAsDataURL會(huì)將文件內(nèi)容進(jìn)行base64編碼后輸出:
document.querySelector('#file').addEventListener('change',(fileChoosed) => { const reader = new FileReader(); reader.readAsDataURL(fileChoosed.target.files[0]);//發(fā)起異步請(qǐng)求 reader.onload = function(readRes){ console.log('加載完成', readRes.target.result) } })
控制臺(tái)為當(dāng)前所傳文件的base64編碼表示。由于媒體文件的src屬性,可以通過(guò)采用網(wǎng)絡(luò)地址或base64的方式顯示,因此我們可以利用readAsDataURL實(shí)現(xiàn)對(duì)圖片的預(yù)覽。
如下,只需將讀取的結(jié)果賦給圖片的src屬性,即可預(yù)覽圖片:
<input type="file" id="file" /> <img src="" id="imgPreview">
document.querySelector('#file').addEventListener('change',(fileChoosed) => { const reader = new FileReader(); reader.readAsDataURL(fileChoosed.target.files[0]);//發(fā)起異步請(qǐng)求 reader.onload = function(readRes){ document.querySelector('#imgPreview').src = readRes.target.result } })
readAsText可按指定編碼方式讀取文件,但讀取文件的單位是字符,故對(duì)于文本文件,只要按規(guī)定的編碼方式讀取即可;而對(duì)于媒體文件(圖片、音頻、視頻),其內(nèi)部組成并不是按字符排列,故采用readAsText讀取,會(huì)產(chǎn)生亂碼。
FileReader事件:
- onloadstart 當(dāng)讀取操作開(kāi)始時(shí)調(diào)用
- onprogress 在讀取數(shù)據(jù)過(guò)程中周期性調(diào)用
- onabort 當(dāng)讀取操作被中止時(shí)調(diào)用
- onerror 當(dāng)讀取操作發(fā)生錯(cuò)誤時(shí)調(diào)用
- onload 當(dāng)讀取操作成功完成時(shí)調(diào)用
- onloadend 當(dāng)讀取操作完成時(shí)調(diào)用,無(wú)論成功,失敗或取消
注釋:
- 每過(guò)50ms左右,就會(huì)觸發(fā)一次progress事件,對(duì)于較大的文件可以利用progress實(shí)現(xiàn)進(jìn)度條;
- 由于種種原因無(wú)法讀取文件時(shí),會(huì)觸發(fā)error事件。觸發(fā)error事件時(shí),相關(guān)信息保存在FileReader對(duì)象的error屬性中,這個(gè)屬性將保存一個(gè)對(duì)象,此對(duì)象只有一個(gè)屬性code,即錯(cuò)誤碼。1表示未找到文件,2表示安全性錯(cuò)誤,3表示讀取中斷,4表示文件不可讀,5表示編碼錯(cuò)誤。
示例:
<input type="file" id="file" />
document.querySelector('#file').addEventListener('change',(fileChoosed) => { const reader = new FileReader(); reader.readAsText(fileChoosed.target.files[0],'utf-8');//發(fā)起異步請(qǐng)求 reader.onloadstart = function(readRes){ // 文件大于500kb則取消加載 if(readRes.total > 1024*500) { reader.abort() } else { console.log("開(kāi)始加載") } } reader.onabort = function (readRes) { console.log('加載已取消') } reader.onprogress = function(readRes){ console.log("加載中", `${(readRes.loaded / readRes.total).toFixed(2)*100}/100`) }
補(bǔ)充:一個(gè)比較全面的例子
實(shí)現(xiàn)的功能:
- 通過(guò)file控件預(yù)覽文本, 圖片
- 非文本, 圖片的以二進(jìn)制串輸出
- 拖拽上述文件實(shí)現(xiàn)預(yù)覽
包含的知識(shí)點(diǎn):
- readAsText方法的使用
- readAsDataURL方法的使用
- readAsBinaryString方法的使用
- 一些Promise的用法也穿插其中
- 簡(jiǎn)單的拖拽用法
為了方便觀看, 這里也把代碼帖上:
<!DOCTYPE html> <html lang="zh-CN"> <head> ? <meta charset="UTF-8"> ? <meta name="viewport" content="width=device-width, initial-scale=1.0"> ? <meta http-equiv="X-UA-Compatible" content="ie=edge"> ? <title>FileReader Learning</title> </head> <body> ? <div class="container"> ? ? <form novalidate> ? ? ? <div class="avatars-row" style="margin: 10px;"> ? ? ? ? <label>選擇文件:? ? ? ? ? ? <input type="file" name="avatars" id="attachment" accept="*/*" multiple></input> ? ? ? ? </label> ? ? ? </div> ? ? ? <div class="result" id="result" style="overflow: auto; border: 1px solid cyan; height: 300px;"></div> ? ? </form> ? </div> ? <script> ? ? (function (window, doc, undefined) { ? ? ? function $(id) { ? ? ? ? return doc.getElementById(id); ? ? ? } ? ? ? let App = { ? ? ? ? createFileReader: () => { ? ? ? ? ? if (typeof FileReader === "undefined") { ? ? ? ? ? ? throw new Error("FileReader is not avaliable"); ? ? ? ? ? } ? ? ? ? ? return new FileReader(); ? ? ? ? }, ? ? ? ? /** ? ? ? ? ?* @return FileList ? ? ? ? ?*/ ? ? ? ? getFiles: () => { ? ? ? ? ? var attachment = $("attachment"), ? ? ? ? ? ? files = attachment.files; ? ? ? ? ? return files || new Error("no file uploaded"); ? ? ? ? }, ? ? ? ? /** ? ? ? ? ?* 統(tǒng)一的讀取接口 ? ? ? ? ?*/ ? ? ? ? read: function(api, file, callback) { ? ? ? ? ? let self = this; ? ? ? ? ? return new Promise((resolve, reject) => { ? ? ? ? ? ? let reader = self.createFileReader(); ? ? ? ? ? ? reader.onload = e => { ? ? ? ? ? ? ? resolve(e.target.result); ? ? ? ? ? ? }; ? ? ? ? ? ? reader.onerror = e => { ? ? ? ? ? ? ? reject("read error"); ? ? ? ? ? ? }; ? ? ? ? ? ? reader.onabort = e => { ? ? ? ? ? ? ? reject("abort error"); ? ? ? ? ? ? }; ? ? ? ? ? ? // size <= 1M ? ? ? ? ? ? if (file && file.size > 1 * 1024 * 1024) { ? ? ? ? ? ? ? reject('file is too large'); ? ? ? ? ? ? ? return; ? ? ? ? ? ? } ? ? ? ? ? ? reader[api](file); ? ? ? ? ? }); ? ? ? ? }, ? ? ? ? onFileChange: function(callback) { ? ? ? ? ? let self = this; ? ? ? ? ? $("attachment").onchange = function (e) { ? ? ? ? ? ? callback(self.getFiles()); ? ? ? ? ? }; ? ? ? ? }, ? ? ? ? onDrop: function() { ? ? ? ? ? let self = this; ? ? ? ? ? let resultContainer = $('result'); ? ? ? ? ? document.ondragover = e => { ? ? ? ? ? ? e.preventDefault(); ? ? ? ? ? }; ? ? ? ? ? document.ondrop = e => { ? ? ? ? ? ? e.preventDefault(); ? ? ? ? ? }; ? ? ? ? ? resultContainer.ondragend = e => { ? ? ? ? ? ? e.preventDefault(); ? ? ? ? ? }; ? ? ? ? ? resultContainer.ondrop = e => { ? ? ? ? ? ? e.preventDefault(); ? ? ? ? ? ? let files = e.dataTransfer.files; ? ? ? ? ? ? self.handleAll(files); ? ? ? ? ? }; ? ? ? ? }, ? ? ? ? handleFiles: function(files) { ? ? ? ? ? let self = this; ? ? ? ? ? let p; ? ? ? ? ? let ret = []; ? ? ? ? ? // FileList轉(zhuǎn)數(shù)組 ? ? ? ? ? [].slice.call(files).forEach(function(file, i) { ? ? ? ? ? ? const type = file.type; ? ? ? ? ? ? switch (true) { ? ? ? ? ? ? ? case /text\/\w+/.test(type): ? ? ? ? ? ? ? ? p = self.read("readAsText", file); ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? ? case /image\/\w+/.test(type): ? ? ? ? ? ? ? ? p = self.read("readAsDataURL", file); ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? ? default: ? ? ? ? ? ? ? ? p = self.read("readAsBinaryString", file); ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? } ? ? ? ? ? ? ret.push(p); ? ? ? ? ? }); ? ? ? ? ? return ret; ? ? ? ? }, ? ? ? ? init: function() { ? ? ? ? ? var self = this; ? ? ? ? ? self.onDrop(); ? ? ? ? ? // 這里的onFileChange不能寫(xiě)成Promise, 不然只會(huì)第一次有效, 因?yàn)闋顟B(tài)已經(jīng)是fulfilled了 ? ? ? ? ? self.onFileChange(files => { ? ? ? ? ? ? self.handleAll(files); ? ? ? ? ? }); ? ? ? ? }, ? ? ? ? handleAll: function(files) { ? ? ? ? ? let self = this; ? ? ? ? ? Promise.all(self.handleFiles(files)) ? ? ? ? ? ? .then(results => { ? ? ? ? ? ? ? self.showResults(results); ? ? ? ? ? ? }) ? ? ? ? ? ? .catch(err => { ? ? ? ? ? ? ? console.log(err); ? ? ? ? ? ? }); ? ? ? ? }, ? ? ? ? showResults: results => { ? ? ? ? ? console.log(results); ? ? ? ? ? let resultContainer = $("result"); ? ? ? ? ? results.forEach((result, i) => { ? ? ? ? ? ? // alert(3); ? ? ? ? ? ? switch (true) { ? ? ? ? ? ? ? case result.indexOf("data:image/") >= 0: ? ? ? ? ? ? ? ? resultContainer.innerHTML += `<div><img src="${result}"></div>`; ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? ? default: ? ? ? ? ? ? ? ? resultContainer.innerHTML += `<p>${result}<p>`; ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? } ? ? ? ? ? }); ? ? ? ? } ? ? ? }; ? ? ? App.init(); ? ? })(window, document); ? </script> </body> </html>
總結(jié)
到此這篇關(guān)于JavaScript中FileReader的文章就介紹到這了,更多相關(guān)js中的FileReader內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript 中關(guān)于array的常用方法詳解
這篇文章主要介紹了javascript 中關(guān)于array的常用方法的相關(guān)資料,需要的朋友可以參考下2017-05-05js實(shí)現(xiàn)按鈕控制帶有停頓效果的圖片滾動(dòng)
這篇文章主要介紹了js實(shí)現(xiàn)按鈕控制帶有停頓效果的圖片滾動(dòng),,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-08-08淺析JavaScript中回調(diào)地獄與asyn函數(shù)和await函數(shù)原理
這篇文章主要介紹了JavaScript中回調(diào)地獄與asyn函數(shù)和await函數(shù)原理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2023-01-01JavaScript動(dòng)態(tài)修改彈出窗口大小的方法
這篇文章主要介紹了JavaScript動(dòng)態(tài)修改彈出窗口大小的方法,涉及javascript中window.open方法的使用技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-04-04javascript支持IE和firefox(FF)的漸變透明效果
DataThis可以發(fā)送任何標(biāo)簽,這個(gè)標(biāo)簽沒(méi)有ID也可以,因?yàn)橛玫氖亲远x屬性。2008-10-10jQuery AJAX回調(diào)函數(shù)this指向問(wèn)題
了解JavaScript的人都知道JavaScript的this不總是指向當(dāng)前對(duì)象,函數(shù)或類中的this指向與調(diào)用這個(gè)函數(shù)的對(duì)象以及上下文環(huán)境是息息相關(guān)的。2010-02-02解析arp病毒背后利用的Javascript技術(shù)附解密方法
解析arp病毒背后利用的Javascript技術(shù)附解密方法...2007-08-08js中位數(shù)不足自動(dòng)補(bǔ)位擴(kuò)展padLeft、padRight實(shí)現(xiàn)代碼
這篇文章主要介紹了js中位數(shù)不足自動(dòng)補(bǔ)位擴(kuò)展之padLeft、padRight實(shí)現(xiàn)方法,主要是通過(guò)String.prototype擴(kuò)展實(shí)現(xiàn),需要的朋友可以參考下2020-04-04JS按條件 serialize() 對(duì)應(yīng)標(biāo)簽的使用方法
serialize()方法通過(guò)序列化表單值,創(chuàng)建標(biāo)準(zhǔn)的URL編碼文本字符串,它的操作對(duì)象是代表表單元素集合的jQuery 對(duì)象。下面通過(guò)本文給大家介紹JS按條件 serialize() 對(duì)應(yīng)標(biāo)簽的相關(guān)知識(shí),感興趣的的朋友一起看看吧2017-07-07