您的位置: 翼速应用 > 业内知识 > web前端 > 正文

怎样在Nodejs中实现大文件读写?

你有没有遇到过node读取的文件超过2G,超过读取Blob最大值,出现读取异常的情况?此外,在node中读写文件也需要受服务器RAM的限制,需要分片读取,本文就给大家分享下读写大文件的详细解决方法。


怎样在Nodejs中实现大文件读写?


怎样在Nodejs中实现大文件读写?


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


获取文件大小:


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请求:


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也是前闭后闭的。


以上就是关于怎样在Nodejs中实现大文件读写的详细教程,翼速应用平台内有更多相关资讯,欢迎查阅!

我来说两句

0 条评论

推荐阅读

  • 响应式布局CSS媒体查询设备像素比介绍

    构建响应式网站布局最常见的是流体网格,灵活调整大小的站点布局技术,确保用户在使用的幕上获得完整的体验。响应式设计如何展示富媒体图像,可以通过以下几种方法。

    admin
  • 提升网站的性能快速加载的实用技巧

    网站速度很重要,快速加载的网站会带来更好的用户体验、更高的转化率、更多的参与度,而且在搜索引擎排名中也扮演重要角色,做SEO,网站硬件是起跑线,如果输在了起跑线,又怎么跟同行竞争。有许多方法可提升网站的性能,有一些技巧可以避免踩坑。

    admin
  • 织梦CMS TAG页找不到标签和实现彩色标签解决方法

    织梦cms是我们常见的网站程序系统的一款,在TAG标签中常常遇到的问题也很多。当我们点击 tags.php 页的某个标签的时候,有时会提示:“系统无此标签,可 能已经移除!” 但是我们检查程序后台,以及前台显示页面。这个标签确实存在,如果解决这个问题那?

    admin
  • HTML关于fieldset标签主要的作用

    在前端开发html页面中常用的标签很多,今天为大家带来的是关于HTML中fieldset标签主要的作用说明,根据技术分析HTML

    admin

精选专题