前些時(shí)候在用FileReader讀取文件的時(shí)候, 發(fā)現(xiàn)一個(gè)問題陨晶,如果文件過大不僅耗費(fèi)內(nèi)存猬仁,甚至?xí)x不出來帝璧,于是一時(shí)興起寫了一個(gè)可以同時(shí)讀取多個(gè)文件且在不考慮外部因素下不沒有文件大小上限的API
核心還是借助FileReader對象,具體實(shí)現(xiàn)細(xì)節(jié)如下
實(shí)現(xiàn)同時(shí)讀取多個(gè)文件
針對不同的文件建立獨(dú)立的FileReader對象并用一個(gè)數(shù)組臨時(shí)緩存湿刽,沒什么難度
實(shí)現(xiàn)分段讀取文件
要借助Blob對象的Slice方法的烁,將文件分成多個(gè)段進(jìn)行讀取,并且在FileReader讀取成功的 onload事件中處理開始讀取下一段文件诈闺。
slice 由于同瀏覽器的用法不一樣渴庆,寫了一個(gè)BlobSlice函數(shù)來統(tǒng)一處理
function blobSlice(blob, start, length) {
if(blob.slice) {
return blob.slice(start, length);
} else if (blob.webkitSlice) {
return blob.webkitSlice(start, length);
} else if (blob.mozSlice) {
return blob.mozSlice(start, length);
} else {
return null;
}
}
并且需要建立一個(gè)初始化FileReader的函數(shù),用來綁定onload事件雅镊,并記錄讀取的文件段的起始點(diǎn)襟雷,每讀取一段之后都進(jìn)行判斷確認(rèn)是否要讀下一段,或是已經(jīng)讀取完畢仁烹。同時(shí)耸弄,在這個(gè)事件中用傳入的回調(diào)函數(shù)處理都出來的數(shù)據(jù)
initReader(type, index, step, file, callback) {
var start = 0,
Reader = this,
resolveProcess = function (event) {
Reader.loadedMap[index] = step * start * 1024 + event.loaded;
Reader.loaded = 0;
Reader.loadedMap.forEach(function (loaded) {
Reader.loaded += loaded;
}, this);
Reader.allProgress[index] = (Reader.loadedMap[index] / Reader.sizeMap[index] * 100).toFixed(2);
Reader.progress = Reader.progressBar.value = (Reader.loaded / Reader.total * 100).toFixed(2);
console.log("total: "+Reader.total);
console.log("loaded: "+Reader.loaded);
console.log(index+": "+Reader.allProgress[index]+"%");
console.log("\n");
},
readBlob = function (type, index, step, file) {
var blob = Reader.blobSlice(file, start * step * 1024, (start + 1) * step * 1024);
if (type === Reader.READ_AS_TEXT) {
Reader.reader[index].readAsText(blob);
}
else if (type === Reader.READ_AS_BINARY_STRING) {
Reader.reader[index].readAsBinaryString(blob);
}
else if (type === Reader.READ_AS_ARRAY_BUFFER) {
Reader.reader[index].readAsArrayBuffer(blob);
}
};
Reader.reader[index].onload = function(event) {
//process every line of the read content if it's text
if (type === this.READ_AS_TEXT) {
var view = event.target.result,
charCount = 0;
for (var i = 0; i < view.length; ++i) {
if (view[i] === '\n' || view[i] === '\r' || i === view.length - 1) {
callback(view.slice(charCount, i));
charCount = i;
}
}
} else {
callback(event.target.result);
}
resolveProcess(event);
if (step === 0) {
return;
}
if (Reader.loadedMap[index] < Reader.sizeMap[index]) {
start++
} else {
delete Reader.reader[index];
Reader.fileCount--;
return;
}
readBlob(type, index, step, file);
};
Reader.reader[index].onprogress = function(event) {
resolveProcess(event);
};
}
在init函數(shù)中保存了一個(gè)start變量,利用閉包封裝到onload中卓缰,每次讀取之后判斷已經(jīng)讀取的字節(jié)是否大于等于文件大小计呈,如果不是則加一,并在下次讀取的時(shí)候分割出新的段進(jìn)行讀取征唬。如果讀取完畢捌显,則將該FileReader從緩存中去掉。
至于怎么判斷已經(jīng)讀取的字節(jié)數(shù)鳍鸵,這部分代碼放在了處理文件讀取的進(jìn)度上苇瓣。
在讀取純文本的時(shí)候利用 \n 和 \r 判斷是否處在行尾,這樣來分割出一行進(jìn)行處理
實(shí)現(xiàn)進(jìn)度處理
在進(jìn)度處理上主要利用了onload 和 onprogress 兩個(gè)事件偿乖,用一個(gè)數(shù)組保存各個(gè)文件的大小,同時(shí)用另一個(gè)數(shù)組保存每個(gè)文件讀取出的字節(jié)數(shù)哲嘲。
resolveProcess = function (event) {
Reader.loadedMap[index] = step * start * 1024 + event.loaded;
Reader.loaded = 0;
Reader.loadedMap.forEach(function (loaded) {
Reader.loaded += loaded;
}, this);
Reader.allProgress[index] = (Reader.loadedMap[index] / Reader.sizeMap[index] * 100).toFixed(2);
Reader.progress = Reader.progressBar.value = (Reader.loaded / Reader.total * 100).toFixed(2);
console.log("total: "+Reader.total);
console.log("loaded: "+Reader.loaded);
console.log(index+": "+Reader.allProgress[index]+"%");
console.log("\n");
}
event 就是傳入該函數(shù)的load 和 progress 事件贪薪,至于這段代碼中的progressBar,是封裝在這個(gè)API中的一個(gè)html5 的progress 元素眠副,用來動態(tài)顯示所有文件讀取的進(jìn)度画切。