JavaScript中的FileReader示例詳解
前言:
FileReader是一種異步文件讀取機(jī)制,結(jié)合input:file可以很方便的讀取本地文件。
input:file
在介紹FileReader之前,先簡單介紹input的file類型。
<input type="file" id="file">
input的file類型會渲染為一個按鈕和一段文字。點擊按鈕可打開文件選擇窗口,文字表示對文件的描述(大部分情況下為文件名);file類型的input會有files屬性,保存著文件的相關(guān)信息。
document.querySelector('#file').addEventListener('change',(fileChoosed) => { console.log('fileChoosed', fileChoosed.target.files) })
點擊按鈕上傳一個文件后,在控制臺打印上傳的文件信息,如下:
可以發(fā)現(xiàn)文件信息是一個json對象,由傳入的file對象組成。每個file對象(即每個上傳文件)包含以下屬性:
lastModified:數(shù)值,表示最近一次修改時間的毫秒數(shù);
lastModifiedDate:對象,表示最近一次修改時間的Date對象;
name:本地文件系統(tǒng)中的文件名;
size:文件的字節(jié)大??;
type:字符串,文件的MIME類型;
webkitRelativePath:此處為空;當(dāng)在input上加上webkitdirectory屬性時,用戶可選擇文件夾,此時webkitRelativePath表示文件夾中文件的相對路徑。
<input type="file" id="file" webkitdirectory>
FileReader
以上file對象只獲取到了對文件的描述信息,但沒有獲得文件中的數(shù)據(jù)。我們可以通過html5提供的FileReader讀取到文件中的數(shù)據(jù)。
首先創(chuàng)建一個FileReader實例:
const reader = new FileReader();
FileReader提供了如下方法:
readAsArrayBuffer(file)
按字節(jié)讀取文件內(nèi)容,結(jié)果用ArrayBuffer對象表示
readAsBinaryString(file)
按字節(jié)讀取文件內(nèi)容,結(jié)果為文件的二進(jìn)制串
readAsDataURL(file)
讀取文件內(nèi)容,結(jié)果用data:url的字符串形式表示
readAsText(file,encoding)
按字符讀取文件內(nèi)容,結(jié)果用字符串形式表示
abort()
終止文件讀取操作
readAsDataURL和readAsText較為常用,這里只對這兩者進(jìn)行說明。
readAsDataURL會將文件內(nèi)容進(jìn)行base64編碼后輸出:
document.querySelector('#file').addEventListener('change',(fileChoosed) => { const reader = new FileReader(); reader.readAsDataURL(fileChoosed.target.files[0]);//發(fā)起異步請求 reader.onload = function(readRes){ console.log('加載完成', readRes.target.result) } })
控制臺為當(dāng)前所傳文件的base64編碼表示。由于媒體文件的src屬性,可以通過采用網(wǎng)絡(luò)地址或base64的方式顯示,因此我們可以利用readAsDataURL實現(xiàn)對圖片的預(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ā)起異步請求 reader.onload = function(readRes){ document.querySelector('#imgPreview').src = readRes.target.result } })
readAsText可按指定編碼方式讀取文件,但讀取文件的單位是字符,故對于文本文件,只要按規(guī)定的編碼方式讀取即可;而對于媒體文件(圖片、音頻、視頻),其內(nèi)部組成并不是按字符排列,故采用readAsText讀取,會產(chǎn)生亂碼。
FileReader事件:
- onloadstart 當(dāng)讀取操作開始時調(diào)用
- onprogress 在讀取數(shù)據(jù)過程中周期性調(diào)用
- onabort 當(dāng)讀取操作被中止時調(diào)用
- onerror 當(dāng)讀取操作發(fā)生錯誤時調(diào)用
- onload 當(dāng)讀取操作成功完成時調(diào)用
- onloadend 當(dāng)讀取操作完成時調(diào)用,無論成功,失敗或取消
注釋:
- 每過50ms左右,就會觸發(fā)一次progress事件,對于較大的文件可以利用progress實現(xiàn)進(jìn)度條;
- 由于種種原因無法讀取文件時,會觸發(fā)error事件。觸發(fā)error事件時,相關(guān)信息保存在FileReader對象的error屬性中,這個屬性將保存一個對象,此對象只有一個屬性code,即錯誤碼。1表示未找到文件,2表示安全性錯誤,3表示讀取中斷,4表示文件不可讀,5表示編碼錯誤。
示例:
<input type="file" id="file" />
document.querySelector('#file').addEventListener('change',(fileChoosed) => { const reader = new FileReader(); reader.readAsText(fileChoosed.target.files[0],'utf-8');//發(fā)起異步請求 reader.onloadstart = function(readRes){ // 文件大于500kb則取消加載 if(readRes.total > 1024*500) { reader.abort() } else { console.log("開始加載") } } reader.onabort = function (readRes) { console.log('加載已取消') } reader.onprogress = function(readRes){ console.log("加載中", `${(readRes.loaded / readRes.total).toFixed(2)*100}/100`) }
補(bǔ)充:一個比較全面的例子
實現(xiàn)的功能:
- 通過file控件預(yù)覽文本, 圖片
- 非文本, 圖片的以二進(jìn)制串輸出
- 拖拽上述文件實現(xiàn)預(yù)覽
包含的知識點:
- readAsText方法的使用
- readAsDataURL方法的使用
- readAsBinaryString方法的使用
- 一些Promise的用法也穿插其中
- 簡單的拖拽用法
為了方便觀看, 這里也把代碼帖上:
<!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不能寫成Promise, 不然只會第一次有效, 因為狀態(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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript 中關(guān)于array的常用方法詳解
這篇文章主要介紹了javascript 中關(guān)于array的常用方法的相關(guān)資料,需要的朋友可以參考下2017-05-05淺析JavaScript中回調(diào)地獄與asyn函數(shù)和await函數(shù)原理
這篇文章主要介紹了JavaScript中回調(diào)地獄與asyn函數(shù)和await函數(shù)原理,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2023-01-01javascript支持IE和firefox(FF)的漸變透明效果
DataThis可以發(fā)送任何標(biāo)簽,這個標(biāo)簽沒有ID也可以,因為用的是自定義屬性。2008-10-10jQuery AJAX回調(diào)函數(shù)this指向問題
了解JavaScript的人都知道JavaScript的this不總是指向當(dāng)前對象,函數(shù)或類中的this指向與調(diào)用這個函數(shù)的對象以及上下文環(huán)境是息息相關(guān)的。2010-02-02解析arp病毒背后利用的Javascript技術(shù)附解密方法
解析arp病毒背后利用的Javascript技術(shù)附解密方法...2007-08-08js中位數(shù)不足自動補(bǔ)位擴(kuò)展padLeft、padRight實現(xiàn)代碼
這篇文章主要介紹了js中位數(shù)不足自動補(bǔ)位擴(kuò)展之padLeft、padRight實現(xiàn)方法,主要是通過String.prototype擴(kuò)展實現(xiàn),需要的朋友可以參考下2020-04-04JS按條件 serialize() 對應(yīng)標(biāo)簽的使用方法
serialize()方法通過序列化表單值,創(chuàng)建標(biāo)準(zhǔn)的URL編碼文本字符串,它的操作對象是代表表單元素集合的jQuery 對象。下面通過本文給大家介紹JS按條件 serialize() 對應(yīng)標(biāo)簽的相關(guān)知識,感興趣的的朋友一起看看吧2017-07-07