美文网首页工作生活
文件操作的方法

文件操作的方法

作者: stringtoString | 来源:发表于2019-07-09 17:20 被阅读0次
    • 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("删除成功");
            });
            
            // 删除成功
            

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

    相关文章

      网友评论

        本文标题:文件操作的方法

        本文链接:https://www.haomeiwen.com/subject/lonlhctx.html