前端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