文件操作的方法

  • fs概述

    • 在 NodeJS 中,所有与文件操作都是通过 fs 核心模块来实现的,包括文件目录的创建、删除、查询以及文件的读取和写入,在 fs 模块中,所有的方法都分为同步和异步两种实现,具有 sync 后缀的方法为同步方法,不具有 sync 后缀的方法为异步方法;

    • 文件描述符fd

      • 操作系统会为每个打开的文件分配一个名为文件描述符的数值标识,文件操作使用这些文件描述符来识别与追踪每个特定的文件,Window 系统使用了一个不同但概念类似的机制来追踪资源,为方便用户,NodeJS 抽象了不同操作系统间的差异,为所有打开的文件分配了数值的文件描述符。
      • 在 NodeJS 中,每操作一个文件,文件描述符是递增的,文件描述符一般从 3 开始,因为前面有 012 三个比较特殊的描述符,分别代表 process.stdin(标准输入)、process.stdout(标准输出)和 process.stderr(错误输出)
    • 标识符flag

      • NodeJS 中,标识位代表着对文件的操作方式,如可读、可写、即可读又可写等等,在下面用一张表来表示文件操作的标识位和其对应的含义
      • r 读取文件,如果文件不存在则抛出异常。
      • r+ 读取并写入文件,如果文件不存在则抛出异常。
      • rs 读取并写入文件,指示操作系统绕开本地文件系统缓存。
      • w 写入文件,文件不存在会被创建,存在则清空后写入。
      • wx 写入文件,排它方式打开。
      • w+ 读取并写入文件,文件不存在则创建文件,存在则清空后写入。
      • wx+ 和 w+ 类似,排他方式打开。
      • a 追加写入,文件不存在则创建文件。
      • ax 与 a 类似,排他方式打开。
      • a+ 读取并追加写入,不存在则创建。
      • ax+ 与 a+ 类似,排他方式打开。
    • 文件读取

      • 同步读取文件readFileSync,有两个参数

        • 第一个参数为读取文件的路径或文件描述符;

        • 第二个参数为 options,默认值为 null,其中有 encoding(编码,默认为 null)和 flag(标识位,默认为 r),也可直接传入 encoding;

        • 返回值为文件的内容,如果没有 encoding,返回的文件内容为 Buffer,如果有按照传入的编码解析。

        • 例:

        • const fs = require("fs");
          
          let resultB = fs.readFileSync("./a.txt");
          let resultD = fs.readFileSync("./a.txt", "utf8");
          // 当读取的值为Buffer的时候,可以调用其toString()的方法,也可以将其转为正常的数据格式
          // console.log(resultB.toString());
          console.log(resultB); // <Buffer 68 65 6c 6c 6f 20 46 53>
          console.log(resultD); // Hello FS
          
      • 异步读取readFile, 有三个参数

        • 异步读取方法与readFileSync 的前两个参数相同,最后一个参数为回调函数,函数内有两个参数 err(错误)和 data(数据),该方法没有返回值,回调函数在读取文件成功后执行,第二个参数也可以不传,通过toString()方法解析返回的数据

        • 例:

        • const fs = require("fs");
          
          fs.readFile("a.txt", "utf8", (err, data) => {
              console.log(err); // null
              console.log(data); // Hello FS
          });
          
    • 文件写入

      • 同步写入writeFileSync,有三个参数

        • 第一个参数为写入文件的路径或文件描述符;

        • 第二个参数为写入的数据,类型为 String 或 Buffer;

        • 第三个参数为 options,默认值为 null,其中有 encoding(编码,默认为 utf8)、 flag(标识位,默认为 w)和 mode(权限位,默认为 0o666),也可直接传入 encoding

        • 例:

        • const fs = require("fs");
          
          fs.writeFileSync("./txt.txt", "Hello FS 你好");
          let result = fs.readFileSync("./txt.txt", "utf8");
          
          console.log(result); // Hello FS 你好
          
      • 异步写入writeFile, 有四个参数

        • 异步写入方法 writeFilewriteFileSync 的前三个参数相同,最后一个参数为回调函数,函数内有一个参数 err(错误),回调函数在文件写入数据成功后执行

        • 例:

        • const fs = require("fs");
          
          fs.writeFile("./txt.txt", "Hello FS 你好", err => {
              if (!err) {
                  fs.readFile("./txt.txt", "utf8", (err, data) => {
                      console.log(data); // Hello FS 你好
                  });
              }
          });
          
    • 文件追加写入

      • 同步追加写入appendFileSync有三个参数

        • 第一个参数为写入文件的路径或文件描述符;

        • 第二个参数为写入的数据,类型为 String 或 Buffer;

        • 第三个参数为 options,默认值为 null,其中有 encoding(编码,默认为 utf8)、 flag(标识位,默认为 a)和 mode(权限位,默认为 0o666),也可直接传入 encoding

        • 例:

        • const fs = require("fs");
          
          fs.appendFileSync("./txt.txt", "我是一名程序员");
          let result = fs.readFileSync("./txt.txt", "utf8");
          
          console.log(result); // Hello FS 你好我是一名程序员
          
      • 异步追加写入appendFile,有四个参数

        • 异步追加写入方法 appendFileappendFileSync 的前三个参数相同,最后一个参数为回调函数,函数内有一个参数 err(错误),回调函数在文件追加写入数据成功后执行

        • const fs = require("fs");
          fs.appendFile("./txt.txt", "2333", err => {
              if (!err) {
                  fs.readFile("./txt.txt", "utf8", (err, data) => {
                      console.log(data); // Hello FS 你好我是一名程序员,2333
                  });
              }
          });
          
    • 文件拷贝写入

      • 同步拷贝写入copyFileSync,两个参数

        • 同步拷贝写入方法 copyFileSync 有两个参数,第一个参数为被拷贝的源文件路径,第二个参数为拷贝到的目标文件路径,如果目标文件不存在,则会创建并拷贝

        • 例:

        • const fs = require("fs");
          
          fs.copyFileSync("./txt.txt", "a.txt");
          let result = fs.readFileSync("a.txt", "utf8");
          
          console.log(result); // Hello FS 你好我是一名程序员,2333
          
      • 异步写入拷贝copyFile,两个参数

        • 异步拷贝写入方法 copyFilecopyFileSync 前两个参数相同,最后一个参数为回调函数,在拷贝完成后执行。

        • 例:

        • const fs = require("fs");
          
          fs.copyFile("./txt.txt", "a.txt", () => {
              fs.readFile("a.txt", "utf8", (err, data) => {
                  console.log(data); // Hello FS 你好我是一名程序员,2333
              });
          });
          
    • Buffer(缓冲区)介绍

      • JavaScript 语言没有用于读取或操作二进制数据流的机制。 Buffer 类是作为 Node.js API 的一部分引入的,用于在 TCP(面向连接的、可靠的、基于字节流的传输层通信协议) 流、文件系统操作、以及其他上下文中与八位字节流进行交互或者可以理解为处理二进制数据流;

      • Buffer.alloc

        • 创建的缓冲区是被初始化过的,所有的项都用00填充

        • 例:

        • let buf1 = Buffer.alloc(6);
          //创建长度为6的缓冲区
          console.log(buf1);
          // <Buffer 00 00 00 00 00 00>
          
      • Buffer.allocUnsafe

        • 创建的 Buffer 并没有经过初始化,在内存中只要有闲置的 Buffer 就直接 “抓过来” 使用

        • 例:

        • let buf2 = Buffer.allocUnsafe(6);
          //创建长度为6的缓冲区
          console.log(buf2);
          // <Buffer 00 e7 8f a0 00 00>
          
      • Buffer.allocUnsafe 创建 Buffer 使得内存的分配非常快,但已分配的内存段可能包含潜在的敏感数据,有明显性能优势的同时又是不安全的,所以使用需格外小心

      • Buffer.from

        • 支持三种传参方式

          • 第一个参数为字符串,第二个参数为字符编码,如 ASCIIUTF-8Base64 等等。
          • 传入一个数组,数组的每一项会以十六进制存储为 Buffer 的每一项。
          • 传入一个 Buffer,会将 Buffer 的每一项作为新返回 Buffer 的每一项。
        • 例:

        • // 传入字符串和字符编码
          let buf = Buffer.from("hello", "utf8");
          
          console.log(buf); // <Buffer 68 65 6c 6c 6f>
          
          //传入数组
          
          // 数组成员为十进制数
          let buf = Buffer.from([1, 2, 3]);
          
          console.log(buf); // <Buffer 01 02 03>
          // 数组成员为十六进制数
          let buf = Buffer.from([0xe4, 0xbd, 0xa0, 0xe5, 0xa5, 0xbd]);
          
          console.log(buf); // <Buffer e4 bd a0 e5 a5 bd>
          console.log(buf.toString("utf8")); // 你好
          
        • 在 NodeJS 中不支持 GB2312 编码,默认支持 UTF-8,在 GB2312 中,一个汉字占两个字节,而在 UTF-8 中,一个汉字占三个字节,所以上面 “你好” 的 Buffer 为 6 个十六进制数组成。

        • 例:

        • // 数组成员为字符串类型的数字
          let buf = Buffer.from(["1", "2", "3"]);
          
          console.log(buf); // <Buffer 01 02 03>
          
        • 传入的数组成员可以是任何进制的数值,当成员为字符串的时候,如果值是数字会被自动识别成数值类型,如果值不是数字或成员为是其他非数值类型的数据,该成员会被初始化为 00

          创建的 Buffer 可以通过 toString 方法直接指定编码进行转换,默认编码为 UTF-8

        • 传入Buffer

          • 例:

          • // 传入一个 Buffer
            let buf1 = Buffer.from("hello", "utf8");
            
            let buf2 = Buffer.from(buf1);
            
            console.log(buf1); // <Buffer 68 65 6c 6c 6f>
            console.log(buf2); // <Buffer 68 65 6c 6c 6f>
            console.log(buf1 === buf2); // true
            console.log(buf1[0] === buf2[0]); // false
            
          • 当传入的参数为一个 Buffer 的时候,会创建一个新的 Buffer 并复制上面的每一个成员。

          • Buffer 为引用类型,一个 Buffer 复制了另一个 Buffer 的成员,当其中一个 Buffer 复制的成员有更改,另一个 Buffer 对应的成员会跟着改变,因为指向同一个引用,类似于 “二维数组”。

          • 例:

          • // Buffer 类比二维数组
            let arr1 = [1, 2, [3]];
            let arr2 = arr1.slice();
            
            arr2[2][0] = 5;
            console.log(arr1); // [1, 2, [5]]
            
    • 打开文件,open,四个参数

      • path:文件的路径;

      • flag:标识位;

      • mode:权限位,默认 0o666

      • callback:回调函数,有两个参数 err(错误)和 fd(文件描述符),打开文件后执行

      • 例:

      • const fs = require("fs");
        
        fs.open("./txt.txt", "r", (err, fd) => {
            console.log(fd);
        });
        
    • 关闭文件close

      • close 方法有两个参数,第一个参数为关闭文件的文件描述符 fd,第二参数为回调函数,回调函数有一个参数 err(错误),关闭文件后执行

      • 例:

      • const fs = require("fs");
        
        fs.open("./txt.txt", "r", (err, fd) => {
            fs.close(fd, err => {
                console.log("关闭成功");
            });
        });
        
    • 读取文件read,有六个参数

      • read 方法与 readFile 不同,一般针对于文件太大,无法一次性读取全部内容到缓存中或文件大小未知的情况,都是多次读取到 Buffer 中

      • fd:文件描述符,需要先使用 open 打开;

      • buffer:要将内容读取到的 Buffer;

      • offset:整数,向 Buffer 写入的初始位置;

      • length:整数,读取文件的长度;

      • position:整数,读取文件初始位置;

      • callback:回调函数,有三个参数 err(错误),bytesRead(实际读取的字节数),buffer(被写入的缓存区对象),读取执行完成后执行。

      • 例:

      • const fs = require("fs");
        let buf = Buffer.alloc(6);
        
        // 打开文件
        fs.open("./txt.txt", "r", (err, fd) => {
            // 读取文件
            fs.read(fd, buf, 0, 3, 0, (err, bytesRead, buffer) => {
                console.log(bytesRead);
                console.log(buffer);
        
                // 继续读取
                fs.read(fd, buf, 3, 3, 3, (err, bytesRead, buffer) => {
                    console.log(bytesRead);
                    console.log(buffer);
                    console.log(buffer.toString());
                });
            });
        });
        
    • 同步磁盘缓存fsync,两个参数

      • fsync 方法有两个参数,第一个参数为文件描述符 fd,第二个参数为回调函数,回调函数中有一个参数 err(错误),在同步磁盘缓存后执行。
      • 在使用 write 方法向文件写入数据时,由于不是一次性写入,所以最后一次写入在关闭文件之前应先同步磁盘缓存,fsync 方法将在后面配合 write 一起使用。
    • 写入文件write,六个参数

      • fd:文件描述符,需要先使用 open 打开;

      • buffer:存储将要写入文件数据的 Buffer;

      • offset:整数,从 Buffer 读取数据的初始位置;

      • length:整数,读取 Buffer 数据的字节数;

      • position:整数,写入文件初始位置;

      • callback:回调函数,有三个参数 err(错误),bytesWritten(实际写入的字节数),buffer(被读取的缓存区对象),写入完成后执行

      • 例:

      • // 选择范围写入
        const fs = require("fs");
        let buf = Buffer.from("你还好吗");
        
        // 打开文件
        fs.open("./txt.txt", "r+", (err, fd) => {
            // 读取 buf 向文件写入数据
            fs.write(fd, buf, 3, 6, 3, (err, bytesWritten, buffer) => {
                // 同步磁盘缓存
                fs.fsync(fd, err => {
                    // 关闭文件
                    fs.close(fd, err => {
                        console.log("关闭文件");
                    });
                });
            });
        });
        
        // 这里为了看是否写入成功可直接使用 readFile 方法
        fs.readFile("./txt.txt", "utf8", (err, data) => {
            console.log(data);
        });
        
    • 针对大文件copy

      • copyFileSync和copyFile是将被拷贝文件的数据一次性读取到内存,一次性写入到目标文件中,针对小文件

      • 如果是一个大文件一次性写入不现实,所以需要多次读取多次写入,接下来使用上面的这些方法针对大文件和文件大小未知的情况实现一个 copy 函数

      • 例:

      • // 大文件拷贝
        // copy 方法
        function copy(src, dest, size = 16 * 1024, callback) {
            // 打开源文件
            fs.open(src, "r", (err, readFd) => {
                // 打开目标文件
                fs.open(dest, "w", (err, writeFd) => {
                    let buf = Buffer.alloc(size);
                    let readed = 0; // 下次读取文件的位置
                    let writed = 0; // 下次写入文件的位置
        
                    (function next() {
                        // 读取
                        fs.read(readFd, buf, 0, size, readed, (err, bytesRead) => {
                            readed += bytesRead;
        
                            // 如果都不到内容关闭文件
                            if(!bytesRead) fs.close(readFd, err => console.log("关闭源文件"));
        
                            // 写入
                            fs.write(writeFd, buf, 0, bytesRead, writed, (err, bytesWritten) => {
                                    // 如果没有内容了同步缓存,并关闭文件后执行回调
                                    if (!bytesWritten) {
                                        fs.fsync(writeFd, err => {
                                            fs.close(writeFd, err => return !err && callback());
                                        });
                                    }
                                    writed += bytesWritten;
        
                                    // 继续读取、写入
                                    next();
                                }
                            );
                        });
                    })();
                });
            });
        }
        
    • 获取文件目录stats

      • 文件目录的 Stats 对象存储着关于这个文件或文件夹的一些重要信息,如创建时间、最后一次访问的时间、最后一次修改的时间、文章所占字节和判断文件类型的多个方法等等

      • 同步获取stats方法statSync

        • statSync 方法参数为一个目录的路径,返回值为当前目录路径的 Stats 对象,现在通过 Stats 对象获取 a 目录下的 b 目录下的 c.txt 文件的字节大小,文件内容为 “你好”

        • 例:

        • const fs = require("fs");
          
          let statObj = fs.statSync("a/b/c.txt");
          console.log(statObj.size); // 6
          
      • 同步获取stats方法stat

        • stat 方法的第一个参数为目录的路径,最后一个参数为回调函数,回调函数有两个参数 err(错误)和 Stats 对象,在读取 Stats 后执行,同样实现上面的读取文件字节数的例子。

        • 例:

        • // 异步获取 Stats 对象
          const fs = require("fs");
          
          fs.stat("a/b/c.txt", (err, statObj) => {
              console.log(statObj.size); // 6
          });
          
    • 创建文件目录

      • 同步创建文件目录mkdirSync

        • mkdirSync 方法参数为一个目录的路径,没有返回值,在创建目录的过程中,必须保证传入的路径前面的文件目录都存在,否则会抛出异常。

        • 例:

        • // 同步创建文件目录
          const fs = require("fs");
          
          // 假设已经有了 a 文件夹和 a 下的 b 文件夹
          fs.mkdirSync("a/b/c");
          
      • 异步创建文件目录mkdir

        • mkdir 方法的第一个参数为目录的路径,最后一个参数为回调函数,回调函数有一个参数 err(错误),在执行创建操作后执行,同样需要路径前部分的文件夹都存在。

        • 例:

        • // 异步创建文件目录
          const fs = require("fs");
          
          // 假设已经有了 a 文件夹和 a 下的 b 文件夹
          fs.mkdir("a/b/c", err => {
              if (!err) console.log("创建成功");
          });
          
    • 删除文件目录

      • 同步删除文件目录rmdirSync

        • rmdirSync 方法参数为一个目录的路径,没有返回值,在创建目录的过程中,必须保证传入的路径前面的文件目录都存在,否则会抛出异常。

        • 例:

        • // 同步删除文件目录
          const fs = require("fs");
          
          fs.rmdirSync("./ab");
          
      • 异步删除文件目录rmdir

        • rmdir方法的第一个参数为目录的路径,最后一个参数为回调函数,回调函数有一个参数 err(错误),在执行创建操作后执行,同样需要路径前部分的文件夹都存在。

        • 例:

        • // 同步删除文件目录
          const fs = require("fs");
          
          fs.rmdir("./ab", err => {
              if (!err) console.log("删除成功");
          });
          
    • 读取文件目录

      • 同步读取文件目录readdirSync,有两个参数

        • 第一个参数为目录的路径,传入的路径前部分的目录必须存在,否则会报错;

        • 第二个参数为 options,其中有 encoding(编码,默认值为 utf8),也可直接传入 encoding

        • 返回值为一个存储文件目录中成员名称的数组。

        • 例:

        • // 同步读取目录
          const fs = require("fs");
          
          let data = fs.readdirSync("a/b");
          console.log(data); // [ 'c', 'index.js' ]
          
      • 异步读取文件目录readdir,有两个参数

        • readdir 方法的前两个参数与 readdirSync 相同,第三个参数为一个回调函数,回调函数有两个参数 err(错误)和 data(存储文件目录中成员名称的数组),在读取文件目录后执行

        • 例:

        • // 异步读取目录
          const fs = require("fs");
          
          fs.readdir("a/b", (err, data) => {
              if (!err) console.log(data);
          });
          
          // [ 'c', 'index.js' ]
          
    • 删除文件操作

      • 同步删除文件操作unlinkSync

        • unlinkSync 的参数为要删除文件的路径,现在存在 a 目录和 a 目录下的 index.js 文件,删除 index.js 文件。

        • 例:

        • // 同步删除文件
          const fs = require("fs");
          
          fs.unlinkSync("a/inde.js");
          
      • 同步删除文件操作unlink

        • unlink 方法的第一个参数与 unlinkSync 相同,最后一个参数为回调函数,函数中存在一个参数 err(错误),在删除文件操作后执行。

        • 例:

        • // 异步删除文件
          const fs = require("fs");
          
          fs.unlink("a/index.js", err => {
              if (!err) console.log("删除成功");
          });
          
          // 删除成功
          

如有错误,欢迎不吝指出。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,233评论 6 495
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,357评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,831评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,313评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,417评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,470评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,482评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,265评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,708评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,997评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,176评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,503评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,150评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,391评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,034评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,063评论 2 352

推荐阅读更多精彩内容

  • Node.js 常用工具 util 是一个Node.js 核心模块,提供常用函数的集合,用于弥补核心JavaScr...
    FTOLsXD阅读 531评论 0 2
  • //公共引用 varfs =require('fs'), path =require('path'); 1、读取文...
    才気莮孒阅读 829评论 0 1
  • 一、核心模块和对象 核心模块的意义 常用内置模块path:处理文件路径fs:操作文件系统child_process...
    EndEvent阅读 4,414评论 0 1
  • https://nodejs.org/api/documentation.html 工具模块 Assert 测试 ...
    KeKeMars阅读 6,320评论 0 6
  • 新奇的创意集市在上周已经完美落幕。创意集市在不多堂举行,不多堂:一个充满艺术气息的建筑,里面的摆设与外面的装饰浑然...
    Dligent阅读 249评论 0 1