NodeJs快速开发应用(二)-fs文件处理

141 阅读10分钟

文件读写

fs模块提供了两种类型的API:同步和异步。同步API会阻塞当前线程,直到操作完成,然后返回结果。异步API则不会阻塞当前线程,而是通过回调函数或Promise对象来返回结果。一般来说,异步API的性能更好,更适合网络编程,但是同步API在某些场景下也有其优势,比如简单的脚本或初始化操作。

同步API和异步API的名称通常有一定的规律,比如同步API以Sync结尾,而异步API则没有。例如,读取文件的同步API是fs.readFileSync,而异步API是fs.readFile

写入文件

    ···fs写入文件.js
    /**
     * 需求:
     * 新建一个文件,座右铭.tst,写入内容,三人行,则必有我师焉
     */
     const fs=require("fs");
     fs.writeFile("./座右铭.txt", 'utf8','三人行必有我师焉',err=>{
        if(err){
         console.log("失败")!
         return 
        }
        console.log("写入成功")!
     })

fs异步和同步写入

    const fs=require("fs")
    //异步写入 性能高
    // fs.writeFile('./data2.txt',"异步写入")//同步写入
    fs.writeFileSync('./data.txt',"同步写入")

fs文件流写入

    /**
     * 观书有感
     */
    //1.导入fs
    const fs=require("fs")
    ​
    //创建写入流对象
    const ws=fs.createWriteStream("./观书有感.txt")
    ​
    //3.write
    ws.writable("半亩方塘一鉴开\r\n")
    ws.writable("天光云影共徘徊\r\n")
    ​
    //4.关闭通道
    ws.close();

读取文件

读写文件是fs模块最常用的功能之一。我们可以使用fs.readFileSyncfs.writeFileSync来同步地读写文件,也可以使用fs.readFilefs.writeFile来异步地读写文件。

    const fs = require('fs')
    ​
    /**
     * fs.readFile  异步读取
     * 
     */
    // fs.readFile("./座右铭.txt", 'utf8', (err, data) => {
    //     if (err) {
    //         console.log("err", err)
    //         return
    //     }
    //     console.log("文件读取成功!",data.toString())
    // })//同步读取
    fs.readFileSync("./座右铭.txt", 'utf8', (err, data) => {
        if (err) {
            console.log("err", err)
            return
        }
        console.log("文件读取成功!", data.toString())
    })

fs模块中,所有对文件及目录的操作都可以使用同步与异步这两种方法。例如,在执行读文件操作时,可以使用readFile方法与readFileSync方法。在这些方法中,所有方法名中具有Sync后缀的方法均为同步方法,而不具有Sync后缀的方法均为异步方法。这两者的区别是:同步方法立即返回操作结果,在使用同步方法执行的操作结束之前,不能执行后续代码,代码类似如下所示

var fs = require('fs');
var data = fs.readFileSync('./index.html', 'utf8');
// 等待操作返回结果,然后利用该结果console.log(data);而异步方法将操作结果作为回调函数的参数进行返回,在方法调用之后,可以立即执行后续代码,代码类似如下所示。
var fs = require('fs');
fs.readFile('./index.html', 'utf8', function(err, data) {// 操作结果作为回调函数的第二个参数返回
	console.log(data);
});

在大多数情况下,应该调用异步方法。但是在很少的场景中(例如读取配置文件并启动服务器),应该使用同步方法。 另外,当使用如下所示的方法同时调用多个异步方法时,并不能确保操作结果的返回顺序。

fs.readFile('./file.html', function (err, data) {
//回调函数代码略
});
fs.readFile('./other.html', function (err, data) {
  //回调函数代码略
});

在上述代码中,我们同时执行两个文件的读取操作,但是并不确保哪个操作结果先被返回,这完全取决于程序读取该文件所花费的时间。如果要确保在一个文件读取完毕后再读取另一个文件,我们应该使用如下所示的方法。

fs.readFile('./file.html', function (err, data) {
// 在回调函数中读取另一个文件
fs.readFile('./other.html', function (err, data) {
// 回调函数代码略
});
});

文件流式读取文件

    //1,导入fs
    const fs=require("fs");
    ​
    /**
     * 2.流式读取对象
     * 
     */
    const rs=fs.createReadStream("./座右铭.txt");
    ​
    //3.绑定 data 事件  chunk 块
    rs.on('data',chunk=>{
        console.log("chunk:",chunk)
        console.log("chunk.length:",chunk.length)//122 字节
        console.log("chunk.tostring:",chunk.toString())
    })
    ​
    //4.end 可选事件
    rs.on('end',()=>{
        console.log("读取完成");
    })

注意,在异步读写文件时,我们需要传入一个回调函数,用于处理错误或结果。如果没有错误发生,err参数会是null或undefined,否则会是一个Error对象。我们可以通过判断err参数来处理不同的情况。

另外,在读写文件时,我们还需要指定文件的编码格式,通常是'utf8'。如果不指定编码格式,读取文件时会返回一个Buffer对象,而不是字符串;写入文件时则会把字符串转换为Buffer对象再写入。

fs复制文件

fs.createReadStream复制文件

    /**
     * 需求:
     * 复制 座右铭.txt 
     */
    const fs=require('fs')

    //流式读取  内存空间更小
    const rs=fs.createReadStream("./座右铭.txt")
    ​
    //流式写入
    const ws=fs.createWriteStream("./座右铭3.txt")

    //第二种写入方式
    rs.pipe(ws);

fs文件重命名与移动

fs.rename文件重命名与移动

    //1.导入fs
    const fs = require('fs')
    //2.fs.rename 修改文件文字  重命名
    // fs.rename("./abc.md","./abcd.md",err=>{
    //    if(err){
    //     console.log(err)
    //     return 
    //    }
    //    console.log("重名名成功!")
    // })//文件移动
    fs.rename("./abcd.md", "./测试目录/abcd.md", err => {
        if (err) {
            console.log(err)
            return
        }
        console.log("重名名成功!")
    })
    ​

追加写入数据

fs.appendFile(filename,data,[options],callback) 在appendFile方法中,使用4个参数,其作用及指定方法与writeFile方法所使用的4个参数的作用与指定方法大致相同,区别在于,在options参数值对象中,flag属性的默认属性值为'a'(在文件底部追加写入数据,如果文件不存在,则创建该文件)。

使用appendFile方法的使用示例:

var fs=require('fs');
fs.appendFile('./message.txt','这是追加的数据。','utf8',function(err){
    if(err)console.log('追加文件操作失败。');
    else console.log('追加文件操作成功。');
});

将这段代码保存在应用程序根目录下的app.js脚本文件中,然后在命令行窗口中重新输入“node app.js”命令运行app.js脚本文件,将在应用程序根目录下的message.txt文件的底部追加“这是追加的数据。”字符串,如果message.txt文件不存在,则创建该文件。追加文件成功时将在控制台中输出“追加文件操作成功。”字符串。

在使用同步方式在文件底部追加数据时,可以使用appendFileSync方法:fs.appendFileSync(filename,data,[options])

在appendFileSync方法中,使用三个参数,这三个参数的含义及指定方法与appendFile方法中使用的file参数、data参数与options参数的含义及指定方法完全相同。

fs删除文件

fs模块还可以用于创建删除目录。我们可以使用fs.mkdirSyncfs.rmdirSync来同步地创建删除目录,也可以使用fs.mkdirfs.rmdir来异步地创建删除目录。

//1.导入fs模块
const fs=require('fs')
​
//2.调用unlike 方法
// fs.unlink('./座右铭3.txt',err=>{
//     if(err){
//         console.log("delete failed")
//         return
//     }
//     console.log("delete success")
// })//2.调用rm 方法  rmSync  
fs.rm("./座右铭3.txt",err=>{
    if(err){
        console.log("删除失败")
        return 
    }
    console.log("delete success")
})

文件夹操作

//1.fs
const fs=require('fs')
​
//2-1.创建文件夹 mk  make制作+ dir   directory文件夹
fs.mkdir('./html',err=>{
    if(err){
        console.log("make failed")
        return 
    }
    console.log("make success")
})
​
//2-2 递归创建文件夹
fs.mkdir("./a/b/c",{recursive:true},err=>{
    if(err){
        console.log("make failed")
        return 
    }
    console.log("make success")
})
​
​
//2-3 读取文件夹
fs.readdir("./测试目录",(err,data)=>{
    if(err){
        console.log("reading failed")
        return 
    }
    console.log("reading success",data.toString())
});
​
//2-3 删除文件夹 rmdir  remove移除  未来可能被移除,不建议使用
// fs.rmdir('./html',err=>{
//     if(err){
//         console.log("deleteDir failed",err)
//         return 
//     }
//     console.log("deleteDir success")
// })
​
​
//2-4 递归删除
fs.rm("./html",{recursive:true},err=>{
    if(err){
        console.log("deleteDir failed",err)
        return 
    }
    console.log("deleteDir success")
})

查看某一个资源的状态

//1.导入 fs 模块
const fs=require("fs");
​
//2. stat 方法  status 状态  缩写
fs.stat("./nodeJs学习文档.md",(err,data)=>{
    //文件/文件夹信息(文件名称、创建日期,类型,大小)
    console.log("stat suceess:",data)
    //是不是一个文件
    console.log(data.isFile())
    //是不是一个文件夹
    console.log(data.isDirectory())
})

fs路径

//1.导入fs模块
const fs=require("fs")
​
//相对路径
fs.writeFileSync("./index.html",'loloooooo')
fs.writeFileSync("index.html",'loloooooo')
fs.writeFileSync("index.html",'loloooooo')
​
//绝对路径
fs.writeFileSync("D:/index.html",'love')//如果是C盘 访问可能会权限不够
fs.writeFileSync("/index.html",'love')

fs相对路径bug与解决

const fs=require('fs')
​
//在哪一个文件夹下面创建运行 就会创建在哪个文件夹目录下面
fs.writeFile("./index.html","love",(err)=>{
    err ?console.log("err failed"):console.log("err success")
})
​
//解决  换绝对路径  —  __dirname
console.log("__dirname:",__dirname)
fs.writeFileSync(__dirname+"./index2.html",'love',(err)=>{
    err ?console.log("err failed"):console.log("err success")
})

监视文件变化

有时候,我们需要监视文件的变化,比如当文件被修改或删除时执行一些操作。这时候,我们可以使用fs.watchFilefs.unwatchFile来监视或取消监视文件变化。

下面是一个监视文件变化的示例:

// 监视文件变化  
const filename = 'test.txt';  
fs.watchFile(filename, (curr, prev) => {  
console.log(`${filename} changed`);  
console.log(`Current size: ${curr.size}`);  
console.log(`Previous size: ${prev.size}`);  
});  
  
// 取消监视文件变化  
setTimeout(() => {  
fs.unwatchFile(filename);  
console.log(`Stopped watching ${filename}`);  
}, 10000);  

在这个示例中,我们监视了test.txt文件的变化,并打印了当前和之前的文件大小。然后,在10秒后取消了监视,并打印了一条消息。

注意,在监视文件变化时,我们需要传入一个回调函数,用于处理变化事件。回调函数会接收两个参数:curr和prev,分别表示当前和之前的状态对象。状态对象包含了一些属性,比如size(大小)、mtime(修改时间)、ctime(创建时间)等。

另外,在取消监视文件变化时,我们需要传入相同的回调函数和文件名。如果传入不同的回调函数或文件名,则无法取消监视。

其他fs代码实践

使用writeFile方法复制图片文件

fs.readFile("./fsFile/微信图片_20230307083948.jpg", "base64", (err, data) => {
    fs.writeFile("./fsFile/b.jpg", 'base64', (err, data) => {
        if (err) {
            console.log("写入文件失败!", err)
        } else {
            console.log("写入文件成功!")
        }
    })
})

暂停并恢复文件的读取

可以使用ReadStream对象的pause方法暂停data事件的触发,这同时也意味着停止文件的读取操作,而已经被读取到操作系统缓存区中的数据也将被暂时保存在操作系统缓存区中。在使用了pause方法暂停data事件的触发之后,可以使用ReadStream对象的resume方法恢复data事件的触发,这也意味着继续读取文件中的数据。我们在打开文件后暂停文件的读取,隔一秒种后开始读取文件并在控制台中输出文件内容:

var fs = require('fs');
var readStream = fs.createReadStream('./message.txt');
readStream.pause();
readStream.on('data', function(data) {
    console.log('
获取到的数据为:'+data);
})
setTimeout(function() {
readStream.resume();
}, 1000);

使用createWriteStream方法写入文件

在write方法中,可以使用callback参数来指定当数据被写入完毕时所要调用的回调函数,接下来看一个使用callback指定WriteStream对象的write方法的回调函数的代码示例。修改代码清单6-39中示例代码如代码下所示,在修改后的代码中,我们在使用write方法在anotherMessage.txt文件中写入数据后,同步地在控制台中输入已写入的数据。

var fs=require('fs');
var file = fs.createReadStream('./message.txt');
var out = fs.createWriteStream('./anotherMessage.txt');
file.on('data', function(data) {
    out.write(data);
});
out.on('open',function(fd){
    console.log('需要被写入的文件已被打开。');
});
file.on('end', function() {
    out.end('再见。',function() {
        console.log('文件全部写入完毕。');
        console.log('共写入%d字节数据',out.bytesWritten);
    });
});

其他常用API

除了上面介绍的功能外,fs模块还提供了一些其他常用的API,比如:

  • fs.existsSync(path):检查路径是否存在(同步)
  • fs.exists(path, callback):检查路径是否存在(异步)
  • fs.statSync(path):获取路径的状态信息(同步)
  • fs.stat(path, callback):获取路径的状态信息(异步)
  • fs.readdirSync(path):获取目录下的所有文件名(同步)
  • fs.readdir(path, callback):获取目录下的所有文件名(异步)
  • fs.appendFileSync(file, data):追加数据到文件末尾(同步)
  • fs.appendFile(file, data, callback):追加数据到文件末尾(异步)
  • fs.unlinkSync(path):删除路径(同步)
  • fs.unlink(path, callback):删除路径(异步)

更多详细的API文档,请参考NodeJs官网

注意事项和技巧

在使用fs模块时,有一些注意事项和技巧可以帮助我们更好地利用它:

  • 在使用异步API时,请确保正确处理错误或结果。不要忽略回调函数中的err参数。
  • 在使用同步API时,请确保捕获可能抛出的异常。不要让异常导致程序崩溃。
  • 在使用同步API时,请注意性能影响。不要在高并发或高延迟的场景下使用同步API。
  • 在使用watchFile或unwatchFile时,请确保传入相同的回调函数和文件名。不要造成内存泄漏或无法取消监视。
  • 在使用readFile或writeFile时,请指定正确的编码格式。不要造成乱码或数据丢失。