Node之文件IO

67 阅读4分钟

Node之文件IO

因为 Node 运行在操作系统上,可以通过 Node 操作系统上面的文件。比如对文件的读取和写入。也就是对外部设备的输入和输出。实际上,只有 cpu 和内存属于内部设备,其他像磁盘,显卡,网卡等等都属于外部设备。而且 IO ,也就是输入输出的速度远小于内存和 CPU 的交互速度。

const path = require('path');
const fs = require('fs');
const filename = path.resolve(__dirname, './myfiles/1.txt');
// fs.readFile(filename, {
//     encoding: 'utf-8'
// },
//     (err, data) => {
//         console.log(data);
//     })
// const content = fs.readFileSync(filename, 'utf-8');
// console.log(content);
// console.log('after readFileSync');
async function read() {
    const content = await fs.promises.readFile(filename, 'utf-8');
    console.log(content);
}
read();
console.log('after read');

当我们在读取文件的时候,可以使用 fs.readFile() 来读取,第一个参数是文件路径,第二个参数是一个回调函数。回调函数第一个参数为错误,第二个参数为回调返回的结果。
文件路径使用绝对路径,用 path.resolve() 来传入路径。如果传入相对路径,它会基于命令行的路径来执行。通过 __dirname 当前模块文件目录和后面的文件路径拼接起来可以得到路径。
可以在中间传入一个配置对象或者编码,来指定转换后的编码格式。
可以加一个 Sync 来实现同步的操作,一般不要使用,除了在刚启动时必须运行的一些文件。而且只能运行有限的次数。否则由于js是单线程的,会非常影响性能,导致阻塞。
此外,还可以使用 promise 的模式进行操作。

async function write() {
    // await fs.promises.writeFile(filename, '怎么了,朋友' + os.EOL,{
    //     flag: 'a'
    // });
    // console.log('content written');
    const buffer = Buffer.from('这是一个buffer' + os.EOL, 'utf-8');
    await fs.promises.writeFile(filename, buffer, {
        flag: 'a'
    });
}
write(); 

文件写入使用 writeFile() ,第二个参数传入写入的内容,第三个参数默认为 'utf-8' ,可以传入标识符 a 来表示追加内容,而不是默认的覆盖内容。
文件写入不返回内容。buffer 是一个字节为单位,也就是两个16进制的数字组成。

async function copy(){
    // await fs.promises.copyFile(
    //     path.resolve(__dirname,'./myfiles/1.txt'),
    //     path.resolve(__dirname,'./myfiles/2.txt')
    // )
    const fromFilename=path.resolve(__dirname,'./myfiles/1.txt');
    const content=await fs.promises.readFile(fromFilename,"utf-8");
    const toFilename=path.resolve(__dirname,'./myfiles/2.txt');
    await fs.promises.writeFile(toFilename,content,{
        flag:'a'
    });
    console.log('复制成功');
}
copy();

还可以手动复制文件过去。fs 也提供了复制的 API。

async function state() {
    const filename = path.resolve(__dirname, './myfiles')
    const stat = await fs.promises.stat(filename)
    console.log(stat);
    console.log(stat.isFile());
    console.log(stat.isDirectory());
}
state();

还可以获取文件或者目录的信息。其中包括 size ,占用的字节空间,atime,上次访问时间,mtime,上次内容修改时间,ctime,上次状态修改时间,birthtime,文件创建时间,isDirectory(),是否是目录,isFile() 是否是文件。
注意,文件夹的大小为0,因为文件夹也是一个文件,只是里面存放了指向内部文件的指针,所以字节为0。

async function read() {
  const filename = path.resolve(__dirname, './myfiles')
  const pathes = await fs.promises.readdir(filename)
  console.log(pathes);
}
read();

读取一个目录下的子文件,包括文件夹,因为文件夹也是一个特殊的文件。不能读取子文件再往下的文件。得到的是数组形式,数组中包含了每个文件的字符串名称。

async function mkdir() {
    const filename = path.resolve(__dirname, './myfiles/a')
    await fs.promises.mkdir(filename)
}
mkdir();

可以创建一个目录,创建一个文件可以使用 writeFile 创建一个空文件。

const dirname = path.resolve(__dirname, "./myfiles/b");
async function exists(filename) {
    try {
        await fs.promises.stat(filename);
        return true;
    }
    catch (err) { 
        if (err.code === "ENOENT") {
            //文件不存在
            return false;
        }
        console.dir(err);
    }
}
async function test() {
    const result = await exists(dirname);
    if(result){
        console.log("目录已存在,无需操作");
    }else{
        await fs.promises.mkdir(dirname);
        console.log("目录创建成功");
    }
}
test();

手动判断文件是否存在,利用 exists 方法进行判断。因为官方已经禁止了 exists 的 promise 方法。如果已经存在就不用操作,否则需要创建相关的目录。

class File {
    constructor(filename, name, ext, isFile, size, createTime, updateTime) {
        this.filename = filename;
        this.name = name;
        this.ext = ext;
        this.isFile = isFile;
        this.size = size;
        this.createTime = createTime;
        this.updateTime = updateTime;
    }
    async getContent(isBuffer = false) {
        if (this.isFile) {
            if (isBuffer) {
                return await fs.promises.readFile(filename);
            } else {
                return await fs.promises.readFile(filename, "utf-8");
            }
        }
        return null;
    }
    async getChildren() {
        if (this.isFile) {
            return [];
        }
        let children = await fs.promises.readdir(this.filename);
        children = children.map((name) => {
            const result = path.resolve(this.filename, name);
            return File.getFile(result);
        })
        return Promise.all(children);
    }
    static async getFile(filename) {
        const stat = await fs.promises.stat(filename);
        const name = path.basename(filename);
        const ext = path.extname(filename);
        const isFile = stat.isFile();
        const size = stat.size;
        const createTime = new Date(stat.birthtime);
        const updateTime = new Date(stat.mtime);
        return new File(filename, name, ext, isFile, size, createTime, updateTime);
    }
}
async function readDir(dirname) {
    const file = await File.getFile(dirname);
    return await file.getChildren();
}
async function test() {
    const filename = path.resolve(__dirname, './myfiles/');
    const result = await readDir(filename);
    const datas = await result[2].getChildren();
    console.log(datas);
}
test()

想要实现通过一个目录就得到一个对象结构,里面包含了各种信息。并且可以通过子文件继续查找。里面有各种属性和查找文件和文件夹的方法。
这种类型可以通过类的方式实现,通过里面的各种属性来实现,以及添加一个静态属性来方便在查找子文件的时候进行调用。查找内容,如果是文件夹,返回 null 。查找文件夹,如果是文件,返回 [],否则通过 map 方法可以返回每一个子文件的属性。