1.Buffer缓存区
缓存区: 缓存区就是内存中开辟一块临时区域用于存储需要运算的字节码
从结构上看Buffer非常像一个数组,他的元素为16进制的两位数
1.1. Buffer的基本概念
JavaScript语言自身只有字符串数据类型,没有二进制数据类型,二进制可以存储电脑中任何的数据(比如:一段文字,一张图片,一个视频,在电脑中都是采用二进制的的方式进行存储的)
NodeJS是服务器端在处理像TCP(网络)流或文件流(也叫字节流),必须使用到 二进制数据.因此在NodeJs中增加了一个Buffer类,该类用来创建一个专门存放二进制数据的缓存区
<Buffer e4 bd a0 e9 a5 bf e5 90 97 3f e6 88 91 e5 a5 bd e9 a5 bf e5 95 8a 21>
Buffer是一个和二进制很像的十六进制的字节码.两个段码表示一个字节
一个二进制表示一个位
一个字节是8个二进制位
一个16进制是2的4次方
// 比如e4
// 16进制表示式e4
// 2进制表示 11100100
// 11100100 正好是一个字节
在内存中这个字节码可以直接使用,如果存到硬盘里,会自动转为二进制存储.,这样转二进制会很方便
2.2. Buffer 创建
Buffer是一个全局的类,不需要加载可以直接使用
2.2.1 创建指定长度的缓存区
在node.js中默认使用utf-8编码,一个中文一般占3个字节
// 创建10个字节的缓存区
var buf = new Buffer(10)
// Buffer()的写法已经弃用, 需要改用Buffer.alloc()
// 写入一个字符a
buf.write('a')
console.log(buf)
这个方法已经被废弃,不要在使用了.
2.2.2 指定数组长度创建Buffer
var buf = Buffer.alloc(10)
console.log(buf)
没什么意义,你知道你要存的内容是多大吗?
2.2.3 使用 from 创建Buffer
var str = 'Hello wrold'
// 将一个字符串保存到buffer中
var buf = Buffer.from(str)
console.log(buf)
// 注意from 的方法参数不能是数字
// 类型可以为string, Buffer, ArrayBuffer, Array, or Array-like Object
2.3. 写入缓存区
buf.write("写入缓存区")
2.4. 缓存区复制
let str = 'Hello wrold'
let buf = Buffer.from(str)
let bff = Buffer.alloc(12)
buf.copy(bff)
2.5. 实例常用方法
2.5.1 buf.length
获取buf的长度
buf.length;
2.5.2 buf.toString()
将buf的内容转为字符串
语法
buf.toString([encoding[,start[,end]]])
参数
- encoding 可选 , 指定编码格式 默认utf8
- start 可选, 开始位置 默认最开始
- end 可选 结束位置 默认结束位置
返回值
解码缓冲区数据并使用指定的编码返回字符串
let buf = Buffer.from('我是谁')
// 不传参
console.log(buf.toString())
// 指定编码, 以及转换的数据
console.log(buf.toString('utf8', 3,6))
2.5.3 buf.fill()
语法
buf.fill(value [, start, [, end]])
参数
- value 必须: 初始化数据
- start 初始化开始的位置
- end 初始化结束的位置
2.5.4 buf.slice()
用法与字符串的slice用法一样
let buf = Buffer.from('我是谁')
console.log(buf.slice(-6,-3))
2.5.5 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)
2.6.类常用的方法(静态方法)
2.6.1 Buffer.isBuffer()
判断一个对象是不是Buffer对象
语法
Buffer.isBuffer(obj)
参数
obj 需要判断的对象
返回值
布尔值
let buf = Buffer.from('我是谁')
console.log(Buffer.isBuffer(buf)) // true
2.6.2 Buffer.concat()
语法 Buffer.concat(), 接受一个数组参数, 数组可以说是buffer数组
Buffer.concat([buf, buf2])
返回值:
返回合并后的buffer
let buf = Buffer.from('我')
let buf2 = Buffer.from('是谁')
console.log(buf)
console.log(buf2)
let buf3 = Buffer.concat([buf,buf2])
console.log(buf3.toString())
注意点
- Buffer 是用于处理二进制数据流
- Buffer一个元素就是一个字节;
- 实例类似整数数组, 但和数组不一样的是,一旦实例,那么大小将固定
- 内存不 是由V8分配的, 是C++代码在V8堆外分配的物理内存
2.fs文件模块(掌握)
由于node.js是服务器端的程序,必须要有读写文件操作,在原生JS中没有这样的功能.
在Node中如果要读写文件,必须使用文件系统模块(fs),提供了文件操作的所有功能
js在浏览器端是不能进行I/O操作的,是为了保护客户端的安全, node内置的js模块, 让js能够在在node环境执行的时候, 可以操作服务器上的资源文件,也就是给与js I/O 操作的能力
2.1. 读取文件
文件的读写有两种方式:
第一种方式是将硬盘是上的内容全部读入内容以后才触发回调函数
第二种方式 是流式读取:将数据从硬盘中读取一节就触发一次回调函数,也就是读取一节数据处理一节数据
实现大文件操作
在直接读取中又分为两种,同步读取和异步读取
2.1.1 异步方式读取
fs.rendFile(文件路径, 回调函数)
定义一个回调函数,接受读取到的内容
// 直接读取文件 -- 异步
var fs = require('fs');
fs.readFile("./file1.txt", function (err, data) {
console.log(data)
// err 错误对象,如果有错则有值,没有报错则为null
// data 是Buffer数据流,如果需要显示数据通过data.toString
})
// 可以使用特殊的方式,就是第二个参数传入数据格式,那么data将直接显示数据
var fs = require('fs');
fs.readFile("./file1.txt", "utf-8",function (err, data) {
console.log(data)
})
实例:将服务器上读取的文件内容通过服务器返回给浏览器
// 创建服务器用的模块
const http = require('http');
// 这是读取文件用模块
const fs = require('fs')
// 创建一个服务器
const server = http.createServer(function (req, res) {
// 设置响应头
res.writeHead(200, { 'Content-Type': 'text/html;charset=UTF-8' });
// 读取文本文件,
fs.readFile('./test.txt', function (err, data) {
// 如果数据读取成功,将数据返回
res.end(data);
})
})
// 监听端口
server.listen(3000);
console.log('服务器已经在3000端口启动了')
2.1.2 同步读取文件
同步采用赋值的方式返回数据
几乎所有的fs函数都有同步版本,只需要在异步版本的函数后面添加Sync即可
fs.readFileSync(文件路径)
示例:
// 直接读取文件 -- 同步
var fs = require('fs');
var data = fs.readFileSync("./file1.txt")
console.log(data.toString());
我们发现同步版本并没有报错接受,如果同步有错,系统将会自动报错
,所以异步方法,如果有错系统不会报错,会将报错传给回调自行处理
2.2. 写入文件
2.2.1 异步版本
语法方式:
fs.writeFile('文件名','数据',function(err){ /* 数据写入失败时的回调函数*/ })
示例:
// 写文件 -- 异步
var fs = require('fs');
var data = "<h2>我是通过fs模块写入的文件</h2>"
fs.writeFile("wuwei.html", data, function (err) {
console.log(err)
})
// 可以传入写入写入的字符编码
fs.writeFile("wuwei.html", data, 'utf8' ,function (err) {
console.log(err)
})
// 如果写入的是buffer ,那么就可以忽略编码
const content = Buffer.from("this is a test")
fs.writeFile("wuwei.html", content, function (err) {
console.log(err)
})
2.2.2 同步版本
fs.writeFileSync(文件路径, 数据)
示例:
var data = "<h2>我是通过fs模块写入的文件</h2>"
fs.writeFileSync("wuwei.html", data)
2.3. 追加写入内容
2.3.1 异步追加
fs.appendFile(文件路径, 数据, 回调函数)
示例:
var data = "<h2>我是通过fs模块写入的文件</h2>"
fs.appendFile("./wuwei.html",data,(err) => {
console.log(err)
})
2.3.2 同步追加
fs.appendFileSync(文件路径, 数据)
示例:
var data = "<h2>我是通过fs模块写入的文件</h2>"
fs.appendFileSync("./wuwei.txt",data)
2.4. 读取文件/文件夹信息
2.4.1 语法方式
异步读取
fs.stat("文件名",function(err,stats){ //state是文件的信息对象,包含了常用个文件信息 })
同步读取
const stat = fs.statSync("./node")
2.4.2 示例
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 // 第一次创建时间
}
2.4.3 文件信息对象的方法
语法
stats.isFile()
判断是不是文件
stats.isDirectory()
判断是不是文件夹(目录)
判断是不是文件
var fs = require("fs");
fs.stat("index.json", function (err, state) {
console.log(state.isFile())
})
2.4.4 修改文件名 rename
var fs = require("fs");
fs.rename("./text", 'test.txt', err => {
if(err) throw err
console.log("done!")
})
2.5. 删除文件
语法:
fs.unlink(path,callback)
// 断开连接式的删除文件
示例:
fs.unlink("wuwei.html", function (err) {
if (err) {
throw err
} else {
console.log("删除成功")
}
})
2.6. 新增目录
语法:
fs.mkdir(path,callback)
示例:
fs.mkdir("./wuwei",function(err){
// 创建失败的回调
})
2.7. 读取目录中的文件列表
fs.readdir(path,callback)
示例:
fs.readdir("./wuwei",function(err,list){
console.log(err);
console.log(list)
// list 是读取文件夹列表
})
// 如果文件夹存在的话,list是 一个node文件夹下所有文件以及文件夹的数组集合
// 如果文件夹不存在就是err
2.8. 删除空文件夹
fs.rmdir(path,callback)
示例:
fs.rmdir("./wuwei",function(err){
// err 报错信息
})
只能删除空文件夹,不能删除空文件夹
2.9 监听文件是否改变
// 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
3.fs流读写方式(掌握)
用read读取文件 方法,会将文件整体读入缓存区,然后使用,用write方法写入文件的时候,会将文件整体读入缓存区,在修改后整体写入文件,
也就是说在node中无论read还是write都是把文件视为一个整体.Node需要在内存中开辟与文件相同大小的缓存空间,如果文件小,的确没有什么问题,如果是一个非常大的文件,超过内存大小怎么办?
3.1. Stream 流介绍
应用程序中,流是一种有序的,有起点和终点的字节数据的传输方式.在应用程序中各种对象之间交流与传输数据的的时候,总是先将该对象中所包含的数据转换为各种形式的流数据(即字节数据),在通过流的传输,到达目的对象后在将流数据转换为该对象中可以使用的数据
所有互联网传输数据都是以流的方式传输的,也就是说都是一节一节的
3.2. 流的操作
3.2.1 流式读取
语法
// 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 表示在读取流读取完毕后触发的事件
stream.on("end", function () {
console.log("读取完毕")
})
读取流事件:error 表示在读取流读取错误时后触发的时间
stream.on("end", function () {
console.log("读取完毕")
})
3.2.2 以流的方式写文件
语法
// 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)
})
3.3. fs管道方式(掌握)
3.3.1. 什么 是管道
管道是Node.Js中流的实现机制.
管道(pipe)提供了一个输出流到输入流传入数据的一个机制.通常我们用于一个流中获取数据并将数据传递到另外一个流中.
3.3.2. 管道语法
语法
输出流.pipe(输入流)
作用
管道可以实现对大文件的操作(文件 大小超过内存)
实例: 不采用管道方式复制文件
var fs = require("fs");
// 创建读取流和写入流
var s1 = fs.createReadStream("./file2.txt");
var s2 = fs.createWriteStream("./file3.txt");
// 读一节数据写入一节数据
s1.on("data", function (d) {
s2.write(d)
})
// 读入完成后,写入完成
s1.on("end", function () {
s2.end();
console.log("复制完成")
})
使用管道复制文件
var fs = require("fs");
// 创建读取流和写入流
var s1 = fs.createReadStream("./file2.txt");
var s2 = fs.createWriteStream("./file3.txt");
s1.pipe(s2);
3.4. 链式流
将多个管道连接起来,实现链式操作
输入流.pipe(中转流).pipe(中转流).pipe(...).pipe(输出流)
例子:使用链式流压缩文件
var fs = require("fs");
// 引入压缩模块
var zlib = require('zlib');
// 创建引入引出流
var s1 = fs.createReadStream("./file3.txt");
var s2 = fs.createWriteStream("./file3.txt.zip");
// 链式操作
s1.pipe(zlib.createGzip()).pipe(s2)
理解就好 ,后面基本上都是用框架