javascript 实现分段读取大文件

前些时候在用FileReader读取文件的时候, 发现一个问题,如果文件过大不仅耗费内存,甚至会读不出来,于是一时兴起写了一个可以同时读取多个文件且在不考虑外部因素下不没有文件大小上限的API

核心还是借助FileReader对象,具体实现细节如下

实现同时读取多个文件

针对不同的文件建立独立的FileReader对象并用一个数组临时缓存,没什么难度

实现分段读取文件

要借助Blob对象的Slice方法,将文件分成多个段进行读取,并且在FileReader读取成功的 onload事件中处理开始读取下一段文件。

slice 由于同浏览器的用法不一样,写了一个BlobSlice函数来统一处理

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;

并且需要建立一个初始化FileReader的函数,用来绑定onload事件,并记录读取的文件段的起始点,每读取一段之后都进行判断确认是否要读下一段,或是已经读取完毕。同时,在这个事件中用传入的回调函数处理都出来的数据

    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函数中保存了一个start变量,利用闭包封装到onload中,每次读取之后判断已经读取的字节是否大于等于文件大小,如果不是则加一,并在下次读取的时候分割出新的段进行读取。如果读取完毕,则将该FileReader从缓存中去掉。

至于怎么判断已经读取的字节数,这部分代码放在了处理文件读取的进度上。

在读取纯文本的时候利用 \n 和 \r 判断是否处在行尾,这样来分割出一行进行处理

实现进度处理

在进度处理上主要利用了onload 和 onprogress 两个事件,用一个数组保存各个文件的大小,同时用另一个数组保存每个文件读取出的字节数。

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");