第八节: 内置模块(四): Buffer 缓存区与fs文件模块

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]]])

参数

  1. encoding 可选 , 指定编码格式 默认utf8
  2. start 可选, 开始位置 默认最开始
  3. 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]])

参数

  1. value 必须: 初始化数据
  2. start 初始化开始的位置
  3. 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())

注意点

  1. Buffer 是用于处理二进制数据流
  2. Buffer一个元素就是一个字节;
  3. 实例类似整数数组, 但和数组不一样的是,一旦实例,那么大小将固定
  4. 内存不 是由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. 管道语法
  1. 语法

    输出流.pipe(输入流)

  2. 作用

    管道可以实现对大文件的操作(文件 大小超过内存)


实例: 不采用管道方式复制文件
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)

理解就好 ,后面基本上都是用框架

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

推荐阅读更多精彩内容