前端JS超大文件读取:浏览器与nodejs如何做大文件操作
Author:zhoulujun Date:
浏览器的文件读取
HTML5 的FileReader API 可以让客户端浏览器对用户本地文件进行读取,这样就不再需要上传文件由服务器进行读取了,这大大减轻了服务器的负担,也节省了上传文件所需要的时间。不过在实践中我发现用FileReader.readAsText()可以轻易地处理一个 300k 的日志文件,但当日志文件有 1G、甚至 2G 那么大,浏览器就会崩溃。这是因为readAsText()会一下子把目标文件加载至内存,导致内存超出上限。所以如果 Web 应用常常需要处理大文件时,我们应该使用FileReader.readAsArrayBuffer()来一块一块读取文件。
由于 JavaScript 中的File对象继承自Blob,所以我们完全可以用Blob.slice()方法将文件切成小块来处理,大致思路是:
先取文件的前 10k 内容,转换成文本
从头获取每一行的前 19 个字符,判断是否满足日期的格式,如果满足那么这 19 个字符就是开始时间
再取文件尾部的 10k 内容,转换成文本
同理从尾部内容遍历每一行来获取结束时间
<input type="file" id="file" />
<button id="get-time">Get Time</button>
<script>
document.getElementById('get-time').onclick = function () {
let file = document.getElementById('file').files[0];
let fr = new FileReader();
let CHUNK_SIZE = 10 * 1024;
let startTime, endTime;
let reverse = false;
fr.onload = function () {
let buffer = new Uint8Array(fr.result);
let timeReg = /\d{4}\-\d{2}\-\d{2} \d{2}:\d{2}:\d{2}/;
for (
let i = reverse ? buffer.length - 1 : 0;
reverse ? i > -1 : i < buffer.length;
reverse ? i-- : i++
) {
if (buffer[i] === 10) {
let snippet = new TextDecoder('utf-8').decode(
buffer.slice(i + 1, i + 20)
);
if (timeReg.exec(snippet)) {
if (!reverse) {
startTime = snippet;
reverse = true;
seek();
} else {
endTime = snippet;
alert(`Log time range: ${startTime} ~ ${endTime}`);
}
break;
}
}
}
};
seek();
function seek() {
let start = reverse ? file.size - CHUNK_SIZE : 0;
let end = reverse ? file.size : CHUNK_SIZE;
let slice = file.slice(start, end);
fr.readAsArrayBuffer(slice);
}
};
</script>
Node读取超大文件
nodejs读取文件有:
fs.readFile
fs.createReadStream() & readLine
fs.createReadStream() & event-stream
第一个方法很明显不适用,如果你尝试把超大文件直接读入内存中!
const fs = require('fs')
const readFileTest = () => {
var data = ''
var rs = fs.createReadStream('./video.mp4');
rs.on('data', function(chunk) {
data += chunk;
});
rs.on('end',function(){
console.log(data);
});
rs.on('error', function(err){
console.log(err.stack);
});
}
readFileTest()
分片读取
createReadStream在读取文件的过程中,其实也可以分段读取,这种分段读取的方法也可以做为大文件读取的备选项。特别是在并发读取的时候有一定的优点,可以提升文件读取和处理的速度。
createReadStream接受第二个参数{start,end}。我们可以通过fs.promises.stat来获取文件的大小,然后确定分片,最后分片一次读取,比如:
const readStremfunc = () => {
const readStream = fs.createReadStream(filepath,{start:start,end:end})
readStream.setEncoding('binary')
let data = ''
readStream.on('data', chunk => {
data = data + chunk
})
readStream.end('data', () => {
...
})
}
参考文章:
使用FileReader.readAsArrayBuffer()在浏览器中处理大文件 https://joji.me/zh-cn/blog/processing-huge-files-using-filereader-readasarraybuffer-in-web-browser/
深入浅出Nodejs中的大文件读写 https://juejin.cn/post/7148051371060068389
转载本站文章《前端JS超大文件读取:浏览器与nodejs如何做大文件操作》,
请注明出处:https://www.zhoulujun.cn/html/webfront/SGML/web/2024_0808_9213.html