首页 > 编程语言 >Node.js: fs.readFile/writeFile 和 fs.createReadStream/writeStream 区别

Node.js: fs.readFile/writeFile 和 fs.createReadStream/writeStream 区别

时间:2023-03-29 15:04:18浏览次数:44  
标签:Node function fs readFile file var path readStream


1. 先说说各自的用法:

How do I read files in node.js?

fs = require('fs');
fs.readFile(file, [encoding], [callback]);

// file = (string) filepath of the file to read

 

encoding is an optional parameter that specifies the type of encoding to read the file. Possible encodings are 'ascii', 'utf8', and 'base64'. If no encoding is provided, the default is utf8.

 

callback is a function to call when the file has been read and the contents are ready - it is passed two arguments, error and data. If there is no error, error will be null and data will contain the file contents; otherwise err contains the error message.

So if we wanted to read /etc/hosts and print it to stdout (just like UNIX cat):



fs = require('fs') fs.readFile('/etc/hosts', 'utf8', function (err,data) { if (err) { return console.log(err); } console.log(data); });



 

The contents of /etc/hosts should now be visible to you, provided you have permission to read the file in the first place.

 

Let's now take a look at an example of what happens when you try to read an invalid file - the easiest example is one that doesn't exist.



fs = require('fs');
fs.readFile('/doesnt/exist', 'utf8', function (err,data) {
  if (err) {
    return console.log(err);
  }
  console.log(data);
});


 

This is the output:



{ stack: [Getter/Setter],
  arguments: undefined,
  type: undefined,
  message: 'ENOENT, No such file or directory \'/doesnt/exist\'',
  errno: 2,
  code: 'ENOENT',
  path: '/doesnt/exist' }


 

How to use fs.createReadStream?



var http = require('http');
var fs = require('fs');

http.createServer(function(req, res) {
  // The filename is simple the local directory and tacks on the requested url
  var filename = __dirname+req.url;

  // This line opens the file as a readable stream
  var readStream = fs.createReadStream(filename);

  // This will wait until we know the readable stream is actually valid before piping
  readStream.on('open', function () {
    // This just pipes the read stream to the response object (which goes to the client)
    readStream.pipe(res);
  });

  // This catches any errors that happen while creating the readable stream (usually invalid names)
  readStream.on('error', function(err) {
    res.end(err);
  });
}).listen(8080);



 

2. 区别:

createReadStream是给你一个ReadableStream,你可以听它的'data',一点一点儿处理文件,用过的部分会被GC(垃圾回收),所以占内存少。 readFile是把整个文件全部读到内存里。

nodejs的fs模块并没有提供一个copy的方法,但我们可以很容易的实现一个,比如:



var source = fs.readFileSync('/path/to/source', {encoding: 'utf8'});
fs.writeFileSync('/path/to/dest', source);


 

这种方式是把文件内容全部读入内存,然后再写入文件,对于小型的文本文件,这没有多大问题,比如grunt-file-copy就是这样实现的。但是对于体积较大的二进制文件,比如音频、视频文件,动辄几个GB大小,如果使用这种方法,很容易使内存“爆仓”。理想的方法应该是读一部分,写一部分,不管文件有多大,只要时间允许,总会处理完成,这里就需要用到流的概念

Stream在nodejs中是EventEmitter的实现,并且有多种实现形式,例如:

  • http responses request
  • fs read write streams
  • zlib streams
  • tcp sockets
  • child process stdout and stderr

上面的文件复制可以简单实现一下:


var fs = require('fs');
var readStream = fs.createReadStream('/path/to/source');
var writeStream = fs.createWriteStream('/path/to/dest');

readStream.on('data', function(chunk) { // 当有数据流出时,写入数据
    writeStream.write(chunk);
});

readStream.on('end', function() { // 当没有数据时,关闭数据流
    writeStream.end();
});


 

上面的写法有一些问题,如果写入的速度跟不上读取的速度,有可能导致数据丢失。正常的情况应该是,写完一段,再读取下一段,如果没有写完的话,就让读取流先暂停,等写完再继续,于是代码可以修改为:


var fs = require('fs');
var readStream = fs.createReadStream('/path/to/source');
var writeStream = fs.createWriteStream('/path/to/dest');

readStream.on('data', function(chunk) { // 当有数据流出时,写入数据
    if (writeStream.write(chunk) === false) { // 如果没有写完,暂停读取流
        readStream.pause();
    }
});

writeStream.on('drain', function() { // 写完后,继续读取
    readStream.resume();
});

readStream.on('end', function() { // 当没有数据时,关闭数据流
    writeStream.end();
});

 

或者使用更直接的pipe



// pipe自动调用了data,end等事件
fs.createReadStream('/path/to/source').pipe(fs.createWriteStream('/path/to/dest'));




下面是一个更加完整的复制文件的过程



var fs = require('fs'),
    path = require('path'),
    out = process.stdout;

var filePath = '/Users/chen/Movies/Game.of.Thrones.S04E07.1080p.HDTV.x264-BATV.mkv';

var readStream = fs.createReadStream(filePath);
var writeStream = fs.createWriteStream('file.mkv');

var stat = fs.statSync(filePath);

var totalSize = stat.size;
var passedLength = 0;
var lastSize = 0;
var startTime = Date.now();

readStream.on('data', function(chunk) {

    passedLength += chunk.length;

    if (writeStream.write(chunk) === false) {
        readStream.pause();
    }
});

readStream.on('end', function() {
    writeStream.end();
});

writeStream.on('drain', function() {
    readStream.resume();
});

setTimeout(function show() {
    var percent = Math.ceil((passedLength / totalSize) * 100);
    var size = Math.ceil(passedLength / 1000000);
    var diff = size - lastSize;
    lastSize = size;
    out.clearLine();
    out.cursorTo(0);
    out.write('已完成' + size + 'MB, ' + percent + '%, 速度:' + diff * 2 + 'MB/s');
    if (passedLength < totalSize) {
        setTimeout(show, 500);
    } else {
        var endTime = Date.now();
        console.log();
        console.log('共用时:' + (endTime - startTime) / 1000 + '秒。');
    }
}, 500);



 

可以把上面的代码保存为copy.js试验一下

我们添加了一个递归的setTimeout(或者直接使用setInterval)来做一个旁观者,每500ms观察一次完成进度,并把已完成的大小、百分比和复制速度一并写到控制台上,当复制完成时,计算总的耗费时间。

结合nodejs的readlineprocess.argv等模块,我们可以添加覆盖提示、强制覆盖、动态指定文件路径等完整的复制方法,有兴趣的可以实现一下,实现完成,可以


ln -s /path/to/copy.js /usr/local/bin/mycopy

 

这样就可以使用自己写的mycopy命令替代系统的cp命令。


 

 

 

 

 

 

标签:Node,function,fs,readFile,file,var,path,readStream
From: https://blog.51cto.com/u_8895844/6157027

相关文章

  • Node.js: 认识流stream
    流是Node.js中一个非常重要的概念,也是Node.js之所以适用于I/O密集型场景的重要原因之一。流是Node.js移动数据的方式,流可以是可读的和/或可写的。在Node.js中很多模块都使......
  • Node.js: 如何退出node命令或者node server
    如果是要退出node命令的话,可以使用:$node>9+2332>process.exit()$ 或者$node>9+2332>.exit$ 如果是要退出nodeserver的话,可以使用:  别人是推荐点击两......
  • nvm常用命令切换node
     注意:nvm usenode版本时,要使用管理员权限打开cmd输入命令,否则报错 常用命令nvmls:列出所有已安装的node版本nvmls-remote:列出所有远程服务器的版本(官方node......
  • ctfshow web入门 文件上传 web151-160
    web151经典前端绕过web152简单后端绕过从web153开始,需使用.user.ini从web154开始,对文件内容进行黑名单过滤每个关卡会累计前面关卡的限制重点:了解前后端......
  • 基于FSK调制解调系统的matlab仿真
    1.算法描述        频移键控是利用载波的频率变化来传递数字信息。数字频率调制是数据通信中使用较早的一种通信方式,由于这种调制解调方式容易实现,抗噪声和抗衰......
  • FastDFS并发问题的排查经历
    附件用的fastdf上传和下载的,本地开发时就没考虑过多文件上传就会有并发的问题,比如多个只上传成功了一个或者上传了但是文档内容缺失了,变成0字节。呵。。都是一次难忘的经......
  • [ubifs] mkfs.ubifs max_leb_cnt too low (17244 needed)
    当你遇到这个傻逼问题的时候,可以看参考这个ubi文件系统/ubifilesystem解决方法就是继续加大@$(HOME_PATH)/tools/mkubifs/mkfs.ubifs–F-q-r$(BUILD_PATH)/out/......
  • jenkins配置多版本的Nodejs
      linux安装nodejs,参考网址:https://www.cnblogs.com/xuejie/p/17264906.html(2)解压安装tarxvzfnode-v11.14.0-linux-x64.tar.gz-C/opt(3)修改环境变量vim/etc/p......
  • CUBRID Node.js驱动包1.1版发布
    CUBRIDNode.js驱动包1.1版发布作者:chszsCUBRIDNode.js驱动包1.1版发布了,你可以通过NPM下载node-cubrid。也可以去官网下载:https://github.com/CUBRID/node-cubrid1.1版的......
  • yum卸载的时候要带上参数 "--nodeps"
    今天通告的一个误操作,说DBA在生产上yum安装了一个MySQL,后面又觉得版本不合适,直接yumremove了,导致服务故障异常。复盘是因为,yumremove的时候不单是把他安装的MySQL给卸载......