nodejs学习(二)Buffer、fs


一、  Buffer(缓冲区)

? 从结构上看Buffer非常像一个数组,它的元素为16进制的两位数
? 实际上一个元素就表示内存中的一个字节
? 实际上Buffer中的内存不是通过JavaScript分配的,而是在底层通过C
++申请的。
? 也就是我们可以直接通过Buffer来创建内存中的空间。

  1.Buffer的操作

? 使用Buffer保存字符串
   let str = "你好";
   let buf = Buffer.from(str , "utf-8"); 
    
? 创建指定大小的Buffer对象
    let buf3 = Buffer.alloc(1024*8)

   2.Buffer的转换

? Buffer与字符串间的转换
  – 支持的编码:
    ? ASCII、 UTF-8、 UTF-16LE/UCS-2、 Base64、Binary、 Hex
– 字符串转Buffer
  ? Buffer.from(str , [encoding]);
– Buffer转字符串
  ? buf.toString([encoding] , [start] , [end]);

  3.写入操作

? 向缓冲区中写入字符串
    – buf.write(string[, offset[, length]][, encoding])
? 替换指定索引位置的数据
    – buf[index]
? 将指定值填入到缓冲区的指定位置
    – buf.fill(value[, offset[, end]][, encoding])

  4.读取操作

? 将缓冲区中的内容,转换为一个字符串返回
    – buf.toString([encoding[, start[, end]]])
? 读取缓冲区指定索引的内容
    – buf[index]

  5.其他操作

? 复制缓冲区
    – buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])
? 对缓冲区切片
    – buf.slice([start[, end]])
? 拼接缓冲区
    – Buffer.concat(list[, totalLength])

二、fs(文件系统)

? 在Node中,与文件系统的交互是非常重要的,服务器的本质就将本地的文件发送给远程的客户端
? Node通过fs模块来和文件系统进行交互

? 该模块提供了一些标准文件访问API来打开、读取、写入文件,以及与其互。

? 要使用fs模块,首先需要对其进行加载
    – const fs = require("fs");

  1.同步和异步调用

? fs模块中所有的操作都有两种形式可供选择同步和异步。

? 同步文件系统会阻塞程序的执行,也就是除非操作完毕,否则不会向下执行代码。

? 异步文件系统不会阻塞程序的执行,而是
在操作完成时,通过回调函数将结果返回。

  2.打开和关闭文件

? 打开文件
    – fs.open(path, flags[, mode], callback)
    – fs.openSync(path, flags[, mode])
? 关闭文件
    – fs.close(fd, callback)
    – fs.closeSync(fd)

  3.打开状态

模式 说明
r 读取文件 , 文件不存在则出现异常
r+ 读写文件 , 文件不存在则出现异常
rs 在同步模式下打开文件用于读取
rs+ 在同步模式下打开文件用于读写
w 打开文件用于写操作 , 如果不存在则创建,如果存在则截断
wx 打开文件用于写操作, 如果存在则打开失败
w+ 打开文件用于读写, 如果不存在则创建, 如果存在则截断
wx+ 打开文件用于读写, 如果存在则打开失败
a 打开文件用于追加, 如果不存在则创建
ax 打开文件用于追加, 如果路径存在则失败
a+ 打开文件进行读取和追加, 如果不存在则创建该文件
ax+ 打开文件进行读取和追加,如果路径存在则失败

  4.写入文件

? fs中提供了四种不同的方式将数据写入文件
    – 简单文件写入
    – 同步文件写入
    – 异步文件写入
    – 流式文件写入

  1.简单文件写入

? fs.writeFile(file, data[, options], callback)
? fs.writeFileSync(file, data[, options])
? 参数:
    – file 文件路径
    – data 被写入的内容,可以是String或Buffer
    – options 对象,包含属性(encoding、 mode、flag)
    – callback 回调函数

  demo:

/*
fs.writeFile(file, data[, options], callback)
fs.writeFileSync(file, data[, options])

    file  |  |  |  文件名或文件描述符/要操作的文件路径
    data  |  |  |  |  要写入的数据
    options  |  选项,可以对写入进行一些设置
        encoding  |  默认值: 'utf8'
        mode  默认值: 0o666
        flag  请参阅对文件系统 flags 的支持。 默认值: 'w'。
    callback 
            err  | 
**/

//导入文件模块
var fs = require("fs");


//创建要写入的信息
var msg = "hello world! 你好,世界!";
var msg1 = "第二次写入会不会覆盖第一次的呢?"

//参数分别是文件路径,要写入的信息,编码,回调函数,回调函数需要有一个参数err
//这种是覆盖写入,文件不存在则新建文件后写入,文件存在则覆盖写入
fs.writeFile("./test.txt", msg1, "utf8", function (err) {
        console.log("+++++++++++++++++++++" + err + "============")
        if (err) {

            console.log("写入文件出错,详细信息" + err)
            throw err;

        } else {
            console.log("写入文件成功")

        }


    }
)

  2.同步文件写入

? fs.writeSync(fd, buffer, offset, length[, position])
? fs.writeSync(fd, data[, position[, encoding]])
? 要完成同步写入文件,先需要通过openSync()打开文件来获取
   一个文件描述符,然后在通过writeSync()写入文件。
? 参数
    – fd 文件描述符,通过openSync()获取
    – data 要写入的数据(String 或 Buffer)
    – offset buffer写入的偏移量
    – length 写入的长度
    – position 写入的起始位置
    – encoding 写入编码

  demo:

/*
  手动操作文件的步骤
    1.打开文件:
        fs.openSync(path[, flags[, mode]])
        - path 要打开文件的路径
        - flags 打开文件要操作的类型:默认为r
            r:只读
            w:可写的
         - mode 设置文件的操作权限,一般不传(默认可读可写)

   2.向文件中写入内容
    fs.writeSync(fd, string[, position[, encoding]])
        - fd 文件描述符
        - string  |  要写入的内容被
        - position  写入的其实位置
        - encoding  编码
        返回:  写入的字节数。
        如果 string 是普通的对象,则它必须具有自有的(不是继承的)toString 函数属性。

   3.保存关闭文件
    fs.closeSync(fd)
        fd 关闭文件描述符。 返回 undefined。
*/

var fs = require("fs");


//w模式会覆盖写入
var fd = fs.openSync("test.txt", "w");
console.log("描述符:" + fd);

var length = fs.writeSync(fd, "这是同步写入的内容");
console.log("写入的长度:" + length);

fs.closeSync(fd);

  3.异步文件写入

? fs.write(fd, buffer, offset, length[, position], callback)
? fs.write(fd, data[, position[, encoding]], callback)
? 要使用异步写入文件,先需要通过open()打开文件,然后在回
调函数中通过write()写入。
? 参数:
    – fd 文件描述符
    – data 要写入的数据(String 或 Buffer)
    – offset buffer写入的偏移量
    – length 写入的长度
    – position 写入的起始位置
    – encoding 写入编码

  demo

/*
  手动操作文件的步骤
    1。异步打开文件:
        fs.open(path[, flags[, mode]], callback)
        - path 要打开文件的路径
        - flags 打开文件要操作的类型:默认为r
            r:只读
            w:可写的
         - mode 设置文件的操作权限,一般不传(默认可读可写)
         - callback 回调有两个参数 (err, fd)。
            err:错误对象,如果没有则为null
             fd:文件描述符
         返回值:该方法会返回一个文件描述符作为结果,我们可以通过该操作符来文件进行各种操作
    fs.write(fd, buffer[, offset[, length[, position]]], callback)
        用来异步写入一个文件
    fs.close(fd[, callback])
        -用来关闭文件
*/

var fs = require("fs");
fs.open("异步test.txt", "w", function (err, fd) {
    //判断是否出错
    if (!err) {
        fs.write(fd, "这是异步写入内容", function (err) {
            if (!err) {
                console.log("写入成功");
                fs.close(fd, function (err) {
                    if (!err) {
                        console.log("文件关闭成功");
                    } else {
                        console.log("文件关闭失败");
                    }

                })
            } else {
                console.log("写入失败");
            }

        })
    }

});

console.log("这个应该比异步的先执行完,对吧?")

  4.流式文件写入

? 往一个文件中写入大量数据时,最好的方法之一
是使用流。
? 若要将数据异步传送到文件,首需要使用以下语
法创建一个Writable对象:
    – fs.createWriteStream(path[, options])
? path 文件路径
? options {encoding:"",mode:"",flag:""}
? 一旦你打开了Writable文件流,就可以使用
write()方法来写入它,写入完成后,在调用end()
方法来关闭流。

  demo

//流式写入
//创建可写流
/*
  fs.createWriteStream(path[, options])
    - 可以用来创建一个可写流
    - path :文件路径
    - options 配置参数

 */

var fs = require("fs");
var ws = fs.createWriteStream("writerstream.txt");

//可以通过监听流的打开和关闭事件来监听流的打开和关闭
/*
    on(时间字符,回调函数)
        -可以为对象绑定一个事件
    once(事件字符串,回调函数)
        - 可以为对象绑定一个一次性事件,该事件会将在出发一次后自动回滚失效
 */

ws.once("open", function () {
    console.log("六打开了");

})

ws.on("close", function () {
    console.log("流关闭了");

})

//通过ws像文件输入内容
ws.write("第一次输入");
ws.write("第二次输入");
ws.write("第三次输入");

//关闭流
ws.end()

  5.读取文件

? fs中提供了四种读取文件的方式
    – 简单文件读取
    – 同步文件读取
    – 异步文件读取
    – 流式文件读取

    1.简单文件读取

? fs.readFile(file[, options], callback)
? fs.readFileSync(file[, options])
– 参数:
  ? file 文件路径或文件描述符
  ? options  | 
    – encoding  |  默认 = null
    – flag  默认 = 'r'
  ? callback 回调函数,有两个参数err 、 data
    err 错误对象
    data 返回一个buffer,要转成字符串需要data.tostring

    demo1

var fs = require('fs');
fs.readFile("./test.txt", "utf-8", function (err, data) {
    console.log("data:" + data);
    console.log("err:" + err);
    if (err) {
        console.log("读取文件出错了:错误是" + err)
    } else {
        console.log("读取文件成功,以下为内容:")
        console.log(data)

        //当option的编码没有指定,返回的是一个buffer
        // console.log(data.toString())
    }
})

    demo2:读取并写入零一个文件

var fs = require('fs');
fs.readFile("C:\\Users\\tjp40922\\Desktop\\a.png", function (err, data) {
    console.log("err:" + err);
    if (err) {
        console.log("读取文件出错了:错误是" + err)
    } else {
        console.log("读取文件成功")
        fs.writeFile("copy.png",data,function (err) {
            if (!err){
                console.log("写入成功")

            }else {
                console.log("写入失败。。,原因是:");
                console.log(err)

            }

        })
        
    }
})

    2.同步文件读取

? fs.readSync(fd, buffer, offset, length,
position)
– 参数:
  ? fd 文件描述符
  ? buffer 读取文件的缓冲区
  ? offset buffer的开始写入的位置
  ? length 要读取的字节数
  ? position 开始读取文件的位置

    3.异步文件读取

? fs.read(fd, buffer, offset, length,
position, callback)
– 参数:
    ? fd 文件描述符
    ? buffer 读取文件的缓冲区
    ? offset buffer的开始写入的位置
    ? length 要读取的字节数
    ? position 开始读取文件的位置
    ? callback 回调函数 参数err , bytesRead , buffer

    4.流式文件读取

? 从一个文件中读取大量的数据时,最好的方法之一就是流式读取,这样将把一个文件作为Readable流的形式打开。
? 要从异步从文件传输数据,首先需要通过以下语法创建一个Readable流对象: – fs.createReadStream(path[, options]) ? path 文件路径 ? options {encoding:
"",mode:"",flag:""}
? 当你打开Readable文件流以后,可以通过readable事件和read()请求,或通过data事件处理程序轻松地从它读出。

  demo1

var fs = require("fs");

//创建一个可读流
var rs = fs.createReadStream("writerstream.txt");

//监听流的开启和关闭
rs.once("open", function () {
    console.log("可读流打开了");

})

rs.on("close", function () {
    console.log("可读流关闭了");
})

//如果要读取一个刻度流中的数据,必须为可读流绑定一个data事件,data事件绑定完毕,他会自动开始读取数据
rs.on("data",function (data) {
    console.log(data.toString())
    
})

  demo2:从一个流到另一个流

var fs = require("fs");

//创建一个可读流
var rs = fs.createReadStream("writerstream.txt");
var ws = fs.createWriteStream("tttt.txtt");

//监听流的开启和关闭
rs.once("open", function () {
    console.log("可读流打开了");

})

rs.on("close", function () {
    console.log("可读流关闭了");
    // 在可读流关闭后也要关闭可写流
    ws.end();
})

ws.once("open", function () {
    console.log("可写流打开了");

})

ws.on("close", function () {
    console.log("可写流关闭了");
})


//如果要读取一个刻度流中的数据,必须为可读流绑定一个data事件,data事件绑定完毕,他会自动开始读取数据
rs.on("data", function (data) {
    console.log(data.toString())
    //把可读流的数据写入可写流
    //这边要注意,不能再这里关闭可写流,要不然只会昔日如一次
    ws.write(data);

})

    可以用更加简单的pipe

var fs = require("fs");

//创建一个可读流
var rs = fs.createReadStream("writerstream.txt");
var ws = fs.createWriteStream("xdxx.txtt");

rs.pipe(ws);

  6.其他操作

? 删除目录
    – fs.rmdir(path, callback)
    – fs.rmdirSync(path)
? 重命名文件和目录
    – fs.rename(oldPath, newPath, callback)
    – fs.renameSync(oldPath, newPath)
? 监视文件更改写入
    – fs.watchFile(filename[, options], listener)