Node-文件操作(上)-简单封装复制文件方法、process对象

fs 文件操作模块

Node 只在文件 IO 操作中,提供了同步调用和异步调用两种形式,fs 模块对文件的几乎所有操作都有同步和异步两种形式,两者可以结合使用,但是推荐能使用异步调用解决问题的情况下,少用同步调用。

例如:readFile()readFileSync()

引入包

1
const path = require('path');

同步操作

  • 同步代码会阻塞后续代码执行,效率低
  • 同步 API 需要 try-catch 捕获异常
  • 同步 API 优点代码符合思维逻辑,
  • 按顺序执行,简单执行
1
2
3
4
5
6
7
const fs = require('fs');
try {
const data = fs.readFileSync('./README.md', 'utf8');
console.log(data);
} catch(e){
console.log('读取文件失败');
}

同步读取文件的一个例子

1
2
3
4
5
6
7
8
9
10
const fs = require('fs');
try {
const data1 = fs.readFileSync('./01.txt', 'utf8');
const data2 = fs.readFileSync('./02.txt', 'utf8');
const data2 = fs.readFileSync('./03.txt', 'utf8');
console.log('已完成所有数据的读取');
console.log(data1, data2, data3);
} catch(e){
console.log('读取文件失败');
}

异步操作

异步 API 往往伴随着一个回调函数用来接收,返回值或异常处理。

回调函数的参数中第一个参数一般都是一个 err 对象,用来判定异步 API 是否发生异常。

  • 异步 API 即便没有捕获异常也不会主动抛出异常
  • 异步 API,无法通过 try-catch 捕获异常

一般文件操作中,所有的异步 API,都会在回调函数中提供一个 error 对象

  • 如果操作过程中有异常,则 error 是一个异常对象
  • 如果操作成功,没有问题,则 error 是一个 null
  • 所以,为了判定是否有异常,if (err) {// 处理异常}
1
2
3
4
5
6
7
const fs = require('fs');
fs.readFile("./data/01.txt","utf8", (err, data1) => {
if (err) {
return console.log("失败了");
}
console.log(data1);
});

异步操作中:

  • 开发阶段,使用 throw err 的形式抛出异常
  • 目的是为了快速的定位代码的错误
  • 如果是网站服务器中,这个就不会去 throw err,
  • 一般会有异常处理机制
  • 一般在生产环境,会处理异常,例如记录日志方便排查错误
  • throw err 会直接抛出异常,退出进程

一个例子

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
const fs = require('fs');
// 开始做某一件事
console.log("做一件事");
console.log("需要准备一些做这件事的必备品");
console.log("找人帮我买必备品\n\n");
// 所有的异步任务都会等待其它的同步代码执行结束之后,才会开始执行
fs.readFile("./data/01.txt", "utf8", (err, data1) => {
if (err) {
throw err;
}
console.log("11111111---", data1);
fs.readFile("./data/02.txt", "utf8", (err, data2) => {
if (err) {
throw err;
}
console.log("222222-----", data2);
fs.readFile("./data/03.txt", "utf8", (err, data3) => {
if (err) {
throw err;
}
console.log("3333----", data3);
// 器材备齐,可以做事了
doSomething (data1, data2, data3);
});
});
});
console.log("先干点别的活");
console.log("然后去睡一会");
console.log("收拾收拾准备");
function doSomething(data1, data2, data3) {
console.log("ok, 器材已经准备齐全,可以做这件事了");
}

封装准备器材的这个过程;

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
const fs = require('fs');
// 上面代码的封装
function ready(doSomething){
fs.readFile("./data/01.txt", "utf8", (err, data1) => {
if (err) {
throw err;
}
console.log("11111111---", data1);
fs.readFile("./data/02.txt", "utf8", (err, data2) => {
if (err) {
throw err;
}
console.log("222222-----", data2);
fs.readFile("./data/03.txt", "utf8", (err, data3) => {
if (err) {
throw err;
}
console.log("3333----", data3);
// 器材备齐,可以做事了
doSomething (data1, data2, data3);
});
});
});
}
ready((data1, data2, data3) => {
console.log("ok, 器材已经准备齐全,可以做这件事了");
});

修改封装中的异常处理方式

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
const fs = require('fs');
// 上面代码的封装
function ready(doSomething){
fs.readFile("./data/01.txt", "utf8", (err, data1) => {
if (err) {
return doSomething(err);
}
console.log("11111111---", data1);
fs.readFile("./data/02.txt", "utf8", (err, data2) => {
if (err) {
return doSomething(err);
}
console.log("222222-----", data2);
fs.readFile("./data/03.txt", "utf8", (err, data3) => {
if (err) {
return doSomething(err);
}
console.log("3333----", data3);
// 不发生错误时,第一个参数传递一个 null
doSomething (null, data1, data2, data3);
});
});
});
}
// 封装过后处理错误的过程
ready((err, data1, data2, data3) => {
if (err) {
throw err;
}
console.log("ok, 器材已经准备齐全,可以做这件事了");
});

封装多个异步并行任务

找多个人去做准备工作

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
const fs = require('fs');
// 上面代码的封装
let count = 0;
// 第一个任务
fs.readFile("./data/01.txt", "utf8", (err, data1) => {
if (err) {
throw err;
}
console.log("11111111---", data1);
count++;
if (count === 3){
console.log("ok, 器材已经准备齐全,可以做这件事了");
}
});
// 第二个任务
fs.readFile("./data/02.txt", "utf8", (err, data2) => {
if (err) {
throw err;
}
console.log("222222-----", data2);
count++;
if (count === 3){
console.log("ok, 器材已经准备齐全,可以做这件事了");
}
});
// 第三个任务
fs.readFile("./data/03.txt", "utf8", (err, data3) => {
if (err) {
throw err;
}
console.log("3333----", data3);
count++;
if (count === 3){
console.log("ok, 器材已经准备齐全,可以做这件事了");
}
});

将上面代码继续封装

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
const fs = require('fs');
// 上面代码的封装
let count = 0;
// 第一个任务
fs.readFile("./data/01.txt", "utf8", (err, data1) => {
if (err) {
throw err;
}
console.log("11111111---", data1);
doSomething();
});
// 第二个任务
fs.readFile("./data/02.txt", "utf8", (err, data2) => {
if (err) {
throw err;
}
console.log("222222-----", data2);
doSomething();
});
// 第三个任务
fs.readFile("./data/03.txt", "utf8", (err, data3) => {
if (err) {
throw err;
}
console.log("3333----", data3);
doSomething();
});
function doSomething(){
count++;
if (count === 3){
console.log("ok, 器材已经准备齐全,可以做这件事了");
}
}

以上封装的问题,ready 的过程可能失败,应该告诉做事的,而不是主动抛出来;怎么告诉呢?传给回调函数,于是封装成了下面代码:

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
const fs = require('fs');
const path = require('path');
ready(['./data/01.txt', './data/02.txt', './data/03.txt'], (err, obj) => {
// obj { data1:'', data2: '', data3: '' }
if (err) {
return console.log('执行任务失败了');
}
console.log(obj);
})
// 封装多个并行的异步API
function ready(filePaths, callback) {
const obj = {};
const length = filePaths.length;
let count = 0;
// 当循环执行结束,就说明分配了三个异步任务
for (let filePath of filePaths) {
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) {
return callback(err);
}
obj[path.parse(filePath).name] = data;
count++;
if (count === length) {
callback(null, obj);
}
})
}
}

歌词在控制台一行一行打印 lrc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const fs = require('fs');
const iconv = require('iconv-lite');
fs.readFile('./data/168305.lrc', (err, data) => {
if (err) {
throw err;
}
const lrcStr = iconv.decode(data, 'gbk');
const lines = lrcStr.split('\r\n');
const regex = /^\[(\d{2})\:(\d{2})\.(\d{2})\]\s(.+)$/;
for (let line of lines){
const matches = regex.exec(line);
if (matches) {
const m = parseInt(matches[1]);
const s = parseInt(matches[2]);
const ms = parseInt(matches[3]);
const content = matches[4];
const time = m * 60 * 1000 + s * 1000 + ms;
setTimeout(function() {
console.log(content);
}, time);
}
}
});
  • 同步调用立即执行,会阻塞后续代码继续执行,如果想要捕获异常需要使用 try-catch
  • 异步调用不会阻塞后续代码继续执行,需要回调函数作为额外的参数,通常包含一个错误作为回调函数的第一个参数
  • 异步调用通过判断第一个err对象来处理异常
  • 异步调用结果往往通过回调函数来进行获取

复制文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const fs = require('fs');
copy ('./node.md','./a.md', err => {
if (err) {
throw err;
}
console.log('copy success');
});
function copy(src, dest, callback) {
fs.readFile(src, (err, data) => {
if (err) {
return callback(err);
}
fs.writeFile(dest, data, err => {
if (err){
return callback(err);
}
callback(null);
});
});
}

process

process.argv 可以获取当前通过执行脚本的时候传递的参数,默认结果是一个数组

  • 数组中第 0 项就是 node 的可执行文件的绝对路径
  • 数组中第 1 项就是 执行的当前脚本文件的绝对路径
  • 数组中从第 2 项开始,就是用户通过执行命令传递进来的参数选项,以空格划分
1
const args = process.argv.slice(2);

封装复制文件函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const fs = require('fs');
const args = process.argv.slice(2);
copy (args[0], args[1], err => {
if (err) {
throw err;
}
console.log('copy success!');
});
function copy(src, des, callback) {
fs.readFile(src, (err, data) => {
if (err) {
return callback(err);
}
fs.writeFile(des, data, err => {
if (err) {
return callback(err);
}
callback(null);
});
});
}
感谢您的支持!
0%