Node.js中实现大文件的读写操作方法是什么
Admin 2022-10-09 群英技术资讯 567 次浏览
一、node中的文件读写1.1 常规文件读写
常规的,如果我们要读取一个比较小的文件,可以直接通过:
const fs = require('fs') let data = fs.readFileSync("./test.png") console.log(data,123) //输出data =一般而言,同步的方法不是很推荐,因为js/nodejs是单线程的,同步的方法会阻塞主线程。最新版的node直接提供了fs.promise,可以结合async/await直接使用:
const fs = require('fs') const readFileSync = async () => { let data = await fs.promises.readFile("./test.png") console.log(data,123) } readFileSync() //输出data =这里通过异步的方法调用不会阻塞主线程,多个文件读取的IO也可以并行进行等。
1.2 Stream文件读写
常规的文件读写,我们会把文件一次性的读取到内存中,这种方法时间效率和内存效率都很低,时间效率低是指必须要一次性读取完毕后才能执行后续才做,内存效率低是指必须把这个文件都一次性读取放入内存中,很占用内存。因此这种情况下,我们一般使用Stream来进行文件的读取:
const fs = require('fs') const readFileTest = () => { var data = '' var rs = fs.createReadStream('./test.png'); rs.on('data', function(chunk) { data += chunk; console.log(chunk) }); rs.on('end',function(){ console.log(data); }); rs.on('error', function(err){ console.log(err.stack); }); } readFileTest() // data =通过Steam来进行文件读写,可以提高内存效率和时间效率。
- 内存效率:在处理数据之前,不需要在内存中加载大量(或整个)数据
- 时间效率:一旦有了数据,就可以开始处理,这大大减少开始处理数据的时间,而不必等到整个数据加载完毕再进行处理。
Stream的文件还支持第二种写法:
const fs = require('fs') const readFileTest = () => { var data = '' var chunk; var rs = fs.createReadStream('./test.png'); rs.on('readable', function() { while ((chunk=rs.read()) != null) { data += chunk; }}); rs.on('end', function() { console.log(data) }); }; readFileTest()二、node文件读写RAM和Blob大小的限制
2.1 基础问题
在读取大文件时,会有读取文件大小的限制,比如我们现在在读取一个2.5G的视频文件:
const fs = require('fs') const readFileTest = async () => { let data = await fs.promises.readFile("./video.mp4") console.log(data) } readFileTest()执行上述的代码会报错:
RangeError [ERR_FS_FILE_TOO_LARGE]: File size (2246121911) is greater than 2 GB
我们可能会想到,通过设置option,NODE_OPTIONS='--max-old-space-size=5000',此时5000M>2.5G,但是报错还是没有消失,也就是说通过Options无法改变node读取文件的大小限制。
上述是常规的方式读取大文件,如果通过Steam的方式读取还会有文件大小的限制嘛? 比如:
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()如上方式读取一个2.5G的文件不会有异常,不过要注意的是这边有一个报错:
data += chunk; ^ RangeError: Invalid string length此时是因为data的长度超过了最大限制,比如2048M等。因此在用Steam处理的时候,在对读取结果的保存时,要注意文件的大小,千万不能超过默认的Buffer的最大值。上述这种情况,我们不用data += chunk将数据全部保存在一个大的data中,我们可以边读取边处理。
2.2 分片读取
createReadStream在读取文件的过程中,其实也可以分段读取,这种分段读取的方法也可以做为大文件读取的备选项。特别是在并发读取的时候有一定的优点,可以提升文件读取和处理的速度。
createReadStream接受第二个参数{start,end}。我们可以通过fs.promises.stat来获取文件的大小,然后确定分片,最后分片一次读取,比如:
- 获取文件大小
const info = await fs.promises.stat(filepath) const size = info.size
- 按照指定的SIZE分片(比如128M一个分片)
const SIZE = 128 * 1024 * 1024 let sizeLen = Math.floor(size/SIZE) let total = sizeLen +1 ; for(let i=0;i<=sizeLen;i++){ if(sizeLen ===i){ console.log(i*SIZE,size,total,123) readStremfunc(i*SIZE,size,total) }else{ console.log(i*SIZE,(i+1)*SIZE,total,456) readStremfunc(i*SIZE,(i+1)*SIZE-1,total) } } //分片后【0,128M】,【128M, 256M】...3.实现读取函数
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', () => { ... }) }值得注意的是fs.createReadStream(filepath,{start,end}),start和end是前闭后闭的,比如fs.createReadSteam(filepath,{start:0,end:1023})读取的是[0,1023]一共1024个bit。
三、其他
3.1 扩展浏览器端的大文件读写
前面将了大文件在nodejs中的读取,那么在浏览器端会读取大文件会有什么问题吗?
浏览器在本地读取大文件时,之前有类似FileSaver、StreamSaver等方案,不过在浏览器本身添加了File的规范,使得浏览器本身就默认和优化了Stream的读取。我们不需要做额外的工作,相关的工作:github.com/whatwg/fs。不过不同的版本会有兼容性的问题,我们还是可以通过FileSaver等进行兼容。
3.2 请求静态资源大文件
如果是在浏览器中获取静态资源大文件,一般情况下只需要通过range分配请求即可,一般的CDN加速域名,不管是阿里云还是腾讯云,对于分片请求都支持的很好,我们可以将资源通过cdn加速,然后在浏览器端直接请求cdn加速有的资源。
分片获取cdn静态资源大文件的步骤为,首先通过head请求获取文件大小:
const getHeaderInfo = async (url: string) => { const res: any = await axios.head(url + `?${Math.random()}`); return res?.headers; }; const header = getHeaderInfo(source_url) const size = header['content-length']我们可以从header中的content-length属性中,获取文件的大小。然后进行分片和分段,最后发起range请求:
在headers中指定 range:
const getRangeInfo = async (url: string, start: number, end: number) => { const data = await axios({ method: 'get', url, headers: { range: `bytes=${start}-${end}`, }, responseType: 'blob', }); return data?.data; };bytes=${start}-${end}
,就可以发起分片请求去获取分段资源,这里的start和end也是前闭后闭的。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
目录基本用法监听object使用deep参数重新赋值通过路径监听内部数据初始化变量触发监听回调总结Vue中可以使用监听器监听属性的变化,并根据属性变化作出响应。但一旦涉及到复杂数据的监听(如Object,但数组一般不需要,因为Vue针对数组做了特殊处理)时就比较复杂了,本文解释了使用watch监听属性变化的方法,包括复
获取标签中value的值使用的javascript获取,所以肯定是包含在 script 标签里的,也需要引入jquery标签示例:input id=goodsCount type=text size=2 readonly=readonly class=num-text value=#{num}获取其中value的值:/
区别:1.JS运行在浏览器端,用于用户的交互效果,NodeJS运行在服务器端,用于服务器的操作,例如,Web服务器创建,数据库的操作,文件的操作等2.JS运行在浏览器端,存在多个JS解释器,存在兼容性的问题,而NodeJS只有V8引擎一种解释器,不存在兼容性问题3.两者都有内置对象,自定义对象,有不同的宿主对象 词语解释(js为例):js的内置对象、自定义对象,和宿主对象javasc
这篇文章主要介绍了Selenium执行JavaScript脚本的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
这篇文章主要介绍了React SSR 之限流,React SSR 毕竟涉及到了服务端,有很多服务端特有的问题需要考虑,而限流就是其中之一,本文会通过一个简单的案例来说明,为什么服务端需要进行限流,需要的朋友可以参考下
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008