1.Buffer
缓存区
-
缓存区: 缓存区就是内存中开辟一块临时区域用于存储需要运算的字节码
-
从结构上看Buffer非常像一个数组,他的元素为16进制的两位数
-
Buffer是一个和二进制很像的十六进制的字节码.两个段码表示一个字节,一个二进制表示一个位,一个字节是8个二进制位,一个16进制是2的4次方
2.Buffer
模块
-
Buffer对象是node处理二进制数据的一个接口,它是Node原生提供的全局对象,可以直接使用,不需要require
-
JavaScript比较擅长处理字符串,对于处理二进制数据(比如TCP数据流),就不太擅长。Buffer对象就是为了解决这个问题而设计的。它是一个构造函数,生成的实例代表了V8引擎分配的一段内存,是一个类似数组的对象,成员都为0到255的整数值,即一个8位的字节
-
NodeJS是服务器端在处理像TCP(网络)流或文件流(也叫字节流),必须使用到 二进制数据.因此在NodeJs中增加了一个Buffer类,该类用来创建一个专门存放二进制数据的缓存区
-
在node.js中默认使用utf-8编码,一个中文一般占3个字节
//生成一个256字节的Buffer实例 var bytes = new Buffer(256) //这个方法已经被废弃,需要改用Buffer.alloc() var buf = Buffer.alloc(256)
-
Buffer
作为构造函数,可以用New生成一个实例,可以接受多种形式的参数var str = 'Hello wrold' // 将一个字符串保存到buffer中 var buf = Buffer.from(str) console.log(buf) // 注意from 的方法参数不能是数字 // 类型可以为string, Buffer, ArrayBuffer, Array, or Array-like Object var buff = Buffer.from(buf)//拷贝,直接在内存中开辟空间
-
Buffer
的存取操作[类数组]/*Buffer的存取操作--类数组*/ var buf3 = Buffer.alloc(10)//开辟一个20字节的内存空间 for(let i = 0;i<buf3.length;i++){ buf3[i] = i; console.log("Buffer字节内容",buf3[i].toString()); }
-
Buffer
值的转换Buffer对象与字符串的互相转换,需要指定编码格式,默认utf-8格式
/*Buffer与字符串的转换 buf.toString([encoding[,start[,end]]]) encoding 可选,指定编码格式 默认是utf-8 start 可选, 开始位置 默认最开始 end 可选 结束位置 默认结束位置 */ let buf4 = Buffer.from('你好呀') // 不传参 console.log(buf4.toString()) // 指定编码, 以及转换的数据,一个中文占3个字节 console.log(buf4.toString('utf8', 3,6)) 1.ascii 2.utf8 3.utf16le:UTF-16的小端编码,支持大于U+10000的四字节字符。 4.ucs2:utf16le的别名。 5.base64 6.hex:将每个字节转为两个十六进制字符
- Buffer 是用于处理二进制数据流
- Buffer一个元素就是一个字节;
- 实例类似整数数组, 但和数组不一样的是,一旦实例,那么大小将固定
- 内存不 是由V8分配的, 是C++代码在V8堆外分配的物理内存
3.Buffer
的基本方法
-
Buffer.isEncoding
():返回一个布尔值,表示buffer实例是否为指定编码 -
Buffer.isBuffer()
:接受一个对象作为参数,返回一个布尔值,表示该对象是否为Buffer实例 -
Buffer.byteLength()
:返回字符串实际占据的字节长度,默认编码方式是utf-8 -
Buffer.concat()
:将一组Buffer对象合并为一个Buffer对象如果
Buffer.concat
的参数数组只有一个成员,就直接返回该成员,如果有多个成员,就返回一个多个成员合并的新Buffer对象还可以接受第二个参数,指定合并和
Buffer对象的总长度
,省略该参数时,Node内部会计算出这个值,然后再据此进行合并运算,因此,显示提供这个参数,能提高运行速度
4.Buffer
的实例方法
-
write()方法:可以向指定的
Buffer队形中写入数据,第一个参数时所写入的内容,第二个参数(可省略)是所写入的起始位置(默认从0开始)
,第三个参数(可省略)是编码方式,默认为urf-8 -
slice()方法:返回一个按照指定位置,从原对象切割出来的
Buffer
实例,它的两个参数分别为切割的起始位置和终止位置 -
toString():方法将Buffer实例,按照指定编码(默认为utf8)转为字符串。toString方法可以只返回指定位置内存的内容,它的第二个参数表示起始位置,第三个参数表示终止位置,两者都是从0开始计算。
-
toJSON():toJSON方法将Buffer实例转为JSON对象。如果JSON.stringify方法调用Buffer实例,默认会先调用toJSON方法。
-
buf.fill():填充数据
buf.fill(value [, start, [, end]]) 参数 value 必须: 初始化数据 start 初始化开始的位置 end 初始化结束的位置
-
buf.copy():从目标buf中拷贝内容
buffer.copy(目标buf, 写法buf开始的位置, 拷贝buffer开始位置, 拷贝buffer结束位置) let buf = Buffer.from('你好呀') let buf2 = Buffer.from([0,0,0,0,0]) // 将buf里从下标3开始,到下标6的自己拷贝到 buf2 从buf2第一个字节开始写入 buf.copy(buf2,1, 3,6)
5.Buffer
的实例属性
- length:length属性返回
Buffer
对象所占据的内存长度,该值与对象的内容无关 - 不管写入什么内容,length属性总是返回
Buffer
对象的空间长度,如果想要知道一个字符串所占据的字节长度,可以将其传入Buffer.byteLength方法
- length属性是可写的,但是这回导致未定义的行为,不建议使用,如果想要修改
Buffer
对象的长度,建议使用slice方法返回一个新的Buffer对象
6.fs模块
-
fs是filesystem的缩写,该模块提供本地文件的读写能力,基本上是POSIX文件操作命令的简单包装
-
fs模块的功能非常丰富. 并且,这个模块几乎对所有操作提供异步和同步两种操作方式,供开发者选择。
-
文件读取方式
① 将硬盘是上的内容全部读入内容以后才触发回调函数,又称为直接读取
② 是流式读取:将数据从硬盘中读取一节就触发一次回调函数,也就是读取一节数据处理一节数据,实现大文件操作
③ 直接读取又分为同步读取和异步读取
-
readFile方法用于异步读取数据,使用一个回调函数来接受读取的内容
readFile方法的第一个参数是文件的路径,可以是绝对路径,也可以是相对路径。注意,如果是相对路径,是相对于当前进程所在的路径(process.cwd()),而不是相对于当前脚本所在的路径。
readFile方法的第二个参数是读取完成后的回调函数。该函数的第一个参数是发生错误时的错误对象,第二个参数是代表文件内容的Buffer实例。
//直接读取文件--异步操作 var fs = require('fs'); fs.readFile("./src/demo.txt", function (err, data) { console.log(data) // err 错误对象,如果有错则有值,没有报错则为null // data 是Buffer数据流,如果需要显示数据通过data.toString }) //第二个参数传入数据的格式,那么data将直接显示数据 fs.readFile('./src/demo.txt','utf-8',function(error,data){ console.log(data); })
-
readFileSync方法用于同步读取数据,采用赋值的方式来读取数据
几乎所有的fs函数都有同步版本,只需要在异步版本的函数后面添加Sync即可 readFileSync方法的第一个参数是文件路径,第二个参数可以是一个表示配置的对象,也可以是一个表示文本文件编码的字符串。默认的配置对象是{ encoding: null, flag: 'r' },即文件编码默认为null,读取模式默认为r(只读)。如果第二个参数不指定编码(encoding),readFileSync方法返回一个Buffer实例,否则返回的是一个字符串。
// 同步读取文件 同步采用赋值的方式返回数据 fs.readFileSync(文件路径) var data = fs.readFileSync('./src/demo.txt') console.log(data.toString());
-
同步版本并没有报错接收,如果同步有错,系统将会自动报错,所以异步方法,如果有错系统不会报错,会将报错传给回调自行处理
-
writeFile方法用于异步写入文件。(默认直接覆盖)
// 异步写入文件 // fs.writeFile('文件名','数据',function(err){ /* 数据写入失败时的回调函数*/ }) //可以传入写入的字符的编码,如果是一个Buffer,可以不指定 var datas = "<h2>这是通过fs模块写入的文件</h2>" fs.writeFile('index.html',datas,function(error){ console.log(error); }) fs.writeFile('demo.txt','utf-8',datas,function(error){ console.log(error); })
fs.writeFile(file, data[, options], callback)
1.file
<string>
|<Buffer>
|<URL>
|<integer>
文件名或文件描述符。2.data
<string>
|<Buffer>
|<TypedArray>
|<DataView>
写入的数据3.options
<Object>
|<string>
写入数据的参数①encoding
<string>
|<null>
数据编码格式:默认值: 'utf8'。②mode
<integer>
默认值: 0o666。③flag
<string>
参阅支持的文件系统标志。默认值: 'w'。4.callback
<Function>
回调函数① err
<Error>
-
追加写入内容
//fs.appendFile(文件路径, 数据, 回调函数) var data = "<h2>我是通过fs模块写入的文件</h2>" fs.appendFile("./test.html",data,(err) => { console.log(err) })
-
stat读取文件/文件夹信息 stats文件信息对象
fs.stat("文件名",function(err,stats){ //stats是文件的信息对象,包含了常用个文件信息 }) //同步读取 const stat = fs.statSync("./node") var fs = require("fs"); fs.stat("index.json", function (err, stats) { console.log(stats) }) /* stats对象 Stats { dev: 226428394, mode: 33206, nlink: 1, uid: 0, gid: 0, rdev: 0, blksize: undefined, ino: 10414574138374020, size: 17, //文件大小,字节数 blocks: undefined, atimeMs: 1566988486138.9304, // mtimeMs: 1566625311439.442, ctimeMs: 1566625311439.442, birthtimeMs: 1566625188201.4536, atime: 2019-08-28T10:34:46.139Z, // 上一次文件访问时间 mtime: 2019-08-24T05:41:51.439Z, // 文件内容修改时间 ctime: 2019-08-24T05:41:51.439Z, // 文件状态改变时间 birthtime: 2019-08-24T05:39:48.201Z // 第一次创建时间 }*/
-
stats对象方法
stats.isFile() 判断是不是文件 stats.isDirectory() 判断是不是文件夹(目录)
var fs = require("fs"); fs.stat("index.json", function (err, state) { console.log(stats.isFile()) })
-
rename修改文件名
var fs = require("fs"); fs.rename("./text", 'test.txt', err => { if(err) throw err console.log("done!") })
-
exists(path, callback)
exists方法用来判断给定路径是否存在,然后不管结果如何,都会调用回调函数,回调函数的参数是一个表示文件是否存在的布尔值。
-
mkdir()/readdir()/fs.rmdir() 新增目录和读取目录中的文件列表及删除空文件夹
mkdir方法用于新建目录。接受三个参数,第一个是目录名,第二个是权限值,第三个是回调函数。
readdir方法用于读取目录,返回一个所包含的文件和子目录名字的数组。
process.cwd是指当前进程所在的目录
fs.mkdir("./src",function(err){ // 创建失败的回调 }) fs.readdir("./src",function(err,list){ console.log(err); console.log(list) // list 是读取文件夹列表 }) // 如果文件夹存在的话,list是 一个node文件夹下所有文件以及文件夹的数组集合 // 如果文件夹不存在就是err
-
watchfile/unwatchfile()
watchfile方法监听一个文件,如果该文件发生变化,就会自动触发回调函数。
这里的文件变化, 分为通过代码修改文件和手动修改文件
unwatchfile方法用于解除对文件的监听。
// watch是监听 文件夹内所有的文件变化 fs.watch("./",{ recursive: false // 默认是flase 是否递归监听 }, (changeType, filename) => { console.log(chaneType) // 改变的类型 ,是修改一个文件 还是新增文件亦或是删除文件 console.log(filename) // 修改的文件名字 }) // watchFile 是监听一个文件的变化 fs.watchFile('./node/1.txt',(c,p) => { console.log(p); console.log(c) })
p监听文件改变之前的stats
c监听文件改变之后(即当前文件)的stats
7.同步与异步
-
同步操作会阻塞后面的代码, 后面的代码必须等待同步操作完成后方可执行;异步操作会先执行后面的代码, 等异步操作完成了就回头执行回调函数
-
同步操作的结果直接作为一个值赋给变量
-
异步操作必须有一个回调函数, 回调函数的第一个参数都是错误对象
-
大多数node的异步api都有一个同步版本, api名称就是异步api+Sync
8.文件系统标识
当flag选项采用字符串时,可用以下标志:
-
'a' - 打开文件用于追加。如果文件不存在,则创建该文件。
-
'ax' - 与 'a' 相似,但如果路径已存在则失败。
-
'a+' - 打开文件用于读取和追加。如果文件不存在,则创建该文件。
-
'ax+' - 与 'a+' 相似,但如果路径已存在则失败。
-
'as' - 以同步模式打开文件用于追加。如果文件不存在,则创建该文件。
-
'as+' - 以同步模式打开文件用于读取和追加。如果文件不存在,则创建该文件。
-
'r' - 打开文件用于读取。如果文件不存在,则出现异常。
-
'r+' - 打开文件用于读取和写入。如果文件不存在,则出现异常。
-
'rs+' - 以同步模式打开文件用于读取和写入。指示操作系统绕过本地的文件系统缓存。
-
这对于在 NFS 挂载上打开文件时非常有用,因为它允许跳过可能过时的本地缓存。 它对 I/O 性能有非常实际的影响,因此除非需要,否则不建议使用此标志。
-
.这不会将 fs.open() 或 fsPromises.open() 转换为同步的阻塞调用。 如果需要同步的操作,则应使用 fs.openSync() 之类的。
-
.'w' - 打开文件用于写入。如果文件不存在则创建文件,如果文件已存在则截断文件。
-
.'wx' - 与 'w' 相似,但如果路径已存在则失败。
-
.'w+' - 打开文件用于读取和写入。如果文件不存在则创建文件,如果文件已存在则截断文件。
-
.'wx+' - 与 'w+' 相似,但如果路径已存在则失败。
9.fs读写流方式
-
用read读取文件方法,会将文件整体读入缓存区,然后使用write方法写入文件的时候,会将文件整体读入缓存区,再修改后也会整体写入文件。
-
在node中无论read还是write都是把文件视为一个整体.Node需要在内存中开辟与文件相同大小的缓存空间,会出现文件过大,超过内存大小的情况。
-
stream流:应用程序中,流是一种有序的,有起点和终点的字节数据的传输方式.在应用程序中各种对象之间交流与传输数据的的时候,总是先将该对象中所包含的数据转换为各种形式的流数据(即字节数据),再通过流的传输,到达目的对象后再将流数据转换为该对象中可以使用的数据
-
所有互联网传输数据都是以流的方式传输的,也就是说都是一节一节的
10.流的操作
-
以流的方式读文件
// 1. 创建读取流 var stream = fs.createReadStream(path) // 2. 绑定data事件接受数据 stream.on("data",function(data){ console.log(data) console.log(1) // 打印几次,表示数据分了几次流 }) // 3. 绑定end事件表示读取完毕 stream.on("end",function(){ console.log("数据流读取完毕") }) // 4. 绑定error错误事件 stream.on("error",function(err){ throw err }) var fs = require("fs"); // 创建一个可读流 var stream = fs.createReadStream("./file2.txt"); // 监听data事件,每读一段流数据就执行一次回调函数 stream.on("data", function (data) { console.log(data) })
每一节数据流的长度是65536字节,大小是65536/1024=64kb,也就是每一节的大小是64kb,可以通过data.length查看
读取流事件:end 表示在读取流读取完毕后触发的事件,error 表示在读取流读取错误时后触发的时间
stream.on("end", function () { console.log("读取完毕") })
-
以流的方式写文件
// 1. 创建写入流 var stream = fs.createWriteStream(path) // 2. 写入数据 stream.write("数据1") // 3. 写入完毕后结束写入流 stream.end() // 4. 数据写入完成事件 stream.on("finish",function(){}) // 5. 绑定error出错事件 stream.on("error",function(err){}) var fs = require("fs"); // 创建一个写入流 var stream = fs.createWriteStream("./file3.txt"); // 写入数据 stream.write("面包吃完了"); stream.write("牛奶喝光了"); stream.write("钱也花完了"); stream.write("工作也丢了"); stream.write("最悲催的是我迷路了"); stream.end(); // 已流的方式写入数据必须显示的声明结束 //写入流可以通过监听finish事件,来处理在写入流完成后要做的事情 stream.on("finish", function () { console.log("写入完成") }) //写入流错误事件,error,在写入错误时触发回调 stream.on("error", function (err) { console.log(err) })