深入浅出Nodejs中的大文件读写

2023/9/29 23:24:15

笔者最近在做一些node端的文件读写和分片上传工作,在这个过程中,发现node读取的文件如果超过2G,超过了读取Blob最大值,会出现读取异常,此外在node中读写文件也受服务器RAM的限制等,需要分片读取,本人记录一下遇到的问题以及解决问题的经过。
  • node中的文件读写
  • node文件读写RAM和Blob大小的限制
  • 其他

一、node中的文件读写

1.1 常规文件读写

常规的,如果我们要读取一个比较小的文件,可以直接通过:
const fs = require('fs')
let data = fs.readFileSync("./test.png")
console.log(data,123)
//输出data = <Buffer 89 50 4e ...> 
一般而言,同步的方法不是很推荐,因为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 = <Buffer 89 50 4e ...> 

这里通过异步的方法调用不会阻塞主线程,多个文件读取的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 = <Buffer 89 50 64 ...> 

通过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来获取文件的大小,然后确定分片,最后分片一次读取,比如:

1.获取文件大小

const info = await fs.promises.stat(filepath) const size = info.size 

2.按照指定的SIZE分片(比如128M一个分片)

 const SIZE = 128 * 1024 * 1024let 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](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fjimmywarting%2FStreamSaver.js "https://github.com/jimmywarting/StreamSaver.js")等方案,不过在浏览器本身添加了File的规范,使得浏览器本身就默认和优化了Stream的读取。我们不需要做额外的工作,相关的工作:[github.com/whatwg/fs。](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fwhatwg%2Ffs%25E3%2580%2582 "https://github.com/whatwg/fs%E3%80%82") 不过不同的版本会有兼容性的问题,我们还是可以通过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请求:

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;}; 

在headers中指定 range: bytes=${start}-${end},就可以发起分片请求去获取分段资源,这里的start和end也是前闭后闭的。

最后

最近还整理一份JavaScript与ES的笔记,一共25个重要的知识点,对每个知识点都进行了讲解和分析。能帮你快速掌握JavaScript与ES的相关知识,提升工作效率。



有需要的小伙伴,可以点击下方卡片领取,无偿分享


http://www.jnnr.cn/a/135078.html

相关文章

JavaSE之注解

目录注解的属性类型元注解注解解析模拟junit最后注解的属性类型 注解只能存放简单的数据 如&#xff1a; 基本数据类型 String Class 注解 枚举 以上类型的一维数组 格式&#xff1a; public interface 注解名称 { public 属性类型 属性名(); } public interface MyAn1 {…

百果园再冲刺港交所上市:扩张靠加盟和放贷,余惠勇夫妇为实控人

11月16日&#xff0c;深圳百果园实业股份有限公司&#xff08;下称“百果园”&#xff09;再次在港交所递交上市申请材料。据贝多财经了解&#xff0c;这已经是百果园第二次递交招股书。相较于此前招股书&#xff0c;百果园补充披露了截至2022年6月30日的财务数据等信息。 据招…

[附源码]SSM计算机毕业设计餐厅卫生安全系统JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

[附源码]java毕业设计流浪宠物免费领养系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

视图相关知识的汇总

重点大纲 描述视图创建&#xff0c;改变视图的定义&#xff0c;删除视图通过视图重新找回数据通过视图插入&#xff0c;更新和删除数据创建和使用inline视图执行Top-N 分析什么是视图&#xff1f; 视图是基于一张表或者另一张视图的逻辑表。 视图本身不包含数据。视图被存储在…

数据库高级 II

数据库高级 II 如何保证二叉排序树中的元素是可比较大小的 让元素所属的类实现接口Comparable 比较器: Comparable: 内比较器,类实现该接口后,需要重写其中的抽象方法,在类的内部定义比较规则.观察String对象之间的比较练习:创建Student类型,其中的属性有:id 学号,name 姓名…

Unity 资源热更新

热更新流程 启动游戏根据当前版本号&#xff0c;和平台号去版本服务器上检查是否有热更从热更服务器上下载md5文件&#xff0c;比对需要热更的具体文件列表从热更服务器上下载需要热更的资源&#xff0c;解压到热更资源目录游戏运行加载资源&#xff0c;优先到热更目录中加载&…

【现代密码学原理】——消息认证码(学习笔记)

&#x1f4d6; 前言&#xff1a;消息认证码 MAC&#xff08;Message Authentication Code&#xff09;是经过特定算法后产生的一小段信息&#xff0c;检查某段消息的完整性&#xff0c;以及作身份验证。它可以用来检查在消息传递过程中&#xff0c;其内容是否被更改过&#xff…

【保姆级】新机器部署RabbitMQ

1、登录服务器&#xff0c;如果非root用户则切root用户 sudo su - 2、在/usr/tmp目录上传erlang、rabbitmq安装包 3、将安装包移到/usr/local/目录 mv /usr/tmp/erlang-21.3.8.2-1.el7.x86_64.rpm /usr/local/ mv /usr/tmp/rabbitmq-server-3.7.15-1.el7.noarch.rpm /usr/lo…

股票系统接口是如何进行数据共享的?

股票系统接口系统在量化交易中常见的一种数据挖掘系统&#xff0c;就比如说&#xff0c;如果你想要从别的网站或服务器上获取资源或信息&#xff0c;别人是不会把数据库共享过来的&#xff0c;他只能给你提供一个他们写好的编程方法来获取数据。也就是说通过股票系统接口输入你…

LDO的前世今生

众所周知&#xff0c;开关电源的效率很高&#xff0c;但是输出电压有纹波&#xff0c;噪声很大&#xff0c;不能直接接入单片机控制电路中&#xff0c;而一般选择的方案都是在开关电源的输出端接一级LDO低压差线性稳压电源&#xff0c;可以保证输出到单片机中的电压很稳定&…

高通SDX12:SFE(shortcut-fe)软加速驱动效果调测

背景 USB转PHY RTL8153不支持高通IPA硬加速,所以采用SFE软加速 调试设备为基于Cat.6通信模组的整机 SFE软加速前:UXM环境实际测速100Mbps,设备内部sirq 87% SFE软加速驱动调测 SFE驱动代码路径:sdx12-ap\shortcut-fe\shortcut-fe SFE驱动编译文件路径:sdx12-ap\poky\m…

《计算机导论》课程学习笔记

目录 第一章认识计算机 1.1计算思维概述 1.2冯诺依曼体系结构 1.3计算机硬件组成 1.4计算机软件 1.5计算机操作系统 第一章认识计算机 1.1计算思维概述 1.计算思维能力概念 计算思维能力的核心是问题求解能力。 发现问题寻求解决问题的思路分析比较不同的方案验证方案…

第一个 Go 程序,从 Hello World 开始

1、开发编辑器 Go 采用的是UTF-8编码的文本文件存放源代码&#xff0c;理论上使用任何一款文本编辑器都可以做 Go 语言开发&#xff0c;这里推荐使用 VS Code 和 Goland。 VS Code 是微软开源的编辑器&#xff0c;而 Goland 是 jetbrains 出品的付费IDE。GoLand 开发工具时收…

HDLC协议的特点及功能,让你一看就会

一 HDLC概述 1.1 HDLC的发展历史 高级数据链路控制(High-Level Data Link Control或简称HDLC)&#xff0c;是一个在同步网上传输数据、面向比特的数据链路层协议&#xff0c;它是由国际标准化组织(ISO)根据IBM公司的SDLC(SynchronousData Link Control)协议扩展开发而成的.其最…

python基础语法>>基本数据类型

一个喜欢算法的大三在校学生,每周都会将学到的知识贡献给大家。☁️&#x1f4a1;&#x1f388; 开始之前&#xff0c;不妨休息一下&#xff0c;先看个小动画&#x1f375;&#xff0c;才能激情地去学习&#xff01; 用python的一个小turtle画了一个简易版的图书馆 python语法大…

流程表单初体验

文章目录1. 表单分类2. 动态表单3. 启动带表单的实例4. 查询任务上的表单5. 保存与完成有小伙伴在星球上催了好几次了&#xff0c;今天松哥就来和大家聊一聊流程中的表单。1. 表单分类 整体上来说&#xff0c;我们可以将表单分为三种不同的类型&#xff1a; 动态表单&#xf…

linux备份mysql8.0数据库脚本

文章目录环境要求步骤1、创建一个.sh文件编写shell脚本2、添加定时任务环境要求 linux系统&#xff0c;安装了mysql8.0 步骤 1、创建一个.sh文件编写shell脚本 创建文件的命令&#xff1a; vim ***.shshell文件文件参考自文章 链接 export LANGen_US.UTF-8 #注意&#xf…

[附源码]java毕业设计流浪动物救助系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

EventBridge 生态实践:融合 SLS 构建一体化日志服务

作者&#xff1a; 昶风 引言 阿里云日志服务 SLS 是一款优秀的日志服务产品&#xff0c;提供一站式地数据采集、加工、查询与分析、可视化、告警、消费与投递等服务。对于使用 SLS 的用户业务而言&#xff0c;SLS 上存储的日志信息反映着业务的运行状态&#xff0c;通过适当地…
最新文章