日常的学习笔记,包括 ES6、Promise、Node.js、Webpack、http 原理、Vue全家桶,后续可能还会继续更新 Typescript、Vue3 和 常见的面试题 等等。
在之前的文章中,我们曾讲过 fs模块 ,其中包含 readFile
和 writeFile
两个方法。其中 readFile
是读取文件, writeFile
是写入文件。
下面我们来用这两个方法来实现一个简单的 拷贝操作 。
1 2 3 4 5 6 7 8 9
| const fs = require('fs'); const path = require('path');
fs.readFile(path.resolve(__dirname, './package.json'), (err, data) => { if (err) return console.log('error', err); fs.writeFile(path.resolve(__dirname, './test.json'), data, () => { console.log('success'); }) })
|
这样我们的文件就被拷贝下来了,但是这种操作有一个问题。
readFile
只适合操作小文件 (如 JS、Css 和 json 文件等)*。但是对于一些大文件 *(如 音频、视频 等) ,虽然也可以操作,但是可能会 淹没可用内存。 原因是因为 readFile
是将文件全部读取下来之后,再进行操作的。
这样我们就需要一种新的概念 分片读写,用来操作大型文件。 而分片读写 也就是后端领域中,经常提及的 数据流 (Stream
) 操作。
大文件的分片读写
参考文献 fs 文件系统 | Node.js API文档
如果要使用 node 实现一套文件读写,我们需要用到 fs.open、fs.read、fs.write、fs.close。
其根本的实现思路就是,将需要进行读写的文件进行 边读边写 的操作,这样我们就可以控制读写的 速率。
现在我们先创建一个目标文件 test.js(需要进行拷贝操作的文件),里面随便写一些内容,比如 1234567890。
现在我们就需要进行 读一写一(读一个数字写一个数字) 的操作,然后再产生一个名为 newTest.js 的文件。
我们先来实现一套 单个文字 的操作流程,来了解一下读写操作的原理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| let buf = Buffer.alloc(1);
fs.open(path.resolve(__dirname, "test.js"), "r", function (err, rfd) { fs.read(rfd, buf, 0, 1, 0, function (err, bytesRead) { console.log(bytesRead); console.log(buf); fs.open(path.resolve(__dirname, "newTest.js"), "w", function (err, wfd) { console.log(rfd, wfd); fs.write(wfd, buf, 0, 1, 0, function (err, bytesWritten) { console.log("success"); }); }); }); });
|
现在我们可以根据这种方式,然后使用 发布订阅模式,封装一套 分片读写操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
|
function copy(source, target, cb) { const BUFFER_SIZE = 3; const PATH_SOURCE = path.resolve(__dirname, source); const PATH_TARGET = path.resolve(__dirname, target); let buf = Buffer.alloc(BUFFER_SIZE); let rOffset = 0; let wOffset = 0; fs.open(PATH_SOURCE, "r", function (err, rfd) { if (err) return cb(err); fs.open(PATH_TARGET, "w", function (err, wfd) { if (err) return cb(err); function next() { fs.read(rfd, buf, 0, BUFFER_SIZE, rOffset, function (err, bytesRead) { if (err) return cb(err); if (bytesRead == 0) { let index = 0; let done = () => { if (++index == 2) { cb(); } }; fs.close(wfd, done); fs.close(rfd, done); return; } fs.write(wfd, buf, 0, bytesRead, wOffset, function (err, bytesWritten) { if (err) return cb(err); rOffset += bytesRead; wOffset += bytesWritten; next(); } ); }); } next(); }); }); };
copy("./test.js", "./newTest.js", function (err) { if (err) return console.log(err); console.log("copy success"); });
|
这样我们就完成了一套 简单的 分片读写操作。
但是这种方式会出现 回调地狱 的问题,代码看起来非常难以阅读和维护。
这时我们就需要通过 数据流 (Stream
) 来进行读写操作。
本篇文章由 莫小尚 创作,文章中如有任何问题和纰漏,欢迎您的指正与交流。
您也可以关注我的 个人站点、博客园 和 掘金,我会在文章产出后同步上传到这些平台上。
最后感谢您的支持!