1.Buffer緩存區(qū)
緩存區(qū): 緩存區(qū)就是內(nèi)存中開(kāi)辟一塊臨時(shí)區(qū)域用于存儲(chǔ)需要運(yùn)算的字節(jié)碼
從結(jié)構(gòu)上看Buffer非常像一個(gè)數(shù)組,他的元素為16進(jìn)制的兩位數(shù)
1.1. Buffer的基本概念
JavaScript語(yǔ)言自身只有字符串?dāng)?shù)據(jù)類(lèi)型,沒(méi)有二進(jìn)制數(shù)據(jù)類(lèi)型,二進(jìn)制可以存儲(chǔ)電腦中任何的數(shù)據(jù)(比如:一段文字,一張圖片,一個(gè)視頻,在電腦中都是采用二進(jìn)制的的方式進(jìn)行存儲(chǔ)的)
NodeJS是服務(wù)器端在處理像TCP(網(wǎng)絡(luò))流或文件流(也叫字節(jié)流),必須使用到 二進(jìn)制數(shù)據(jù).因此在NodeJs中增加了一個(gè)Buffer類(lèi),該類(lèi)用來(lái)創(chuàng)建一個(gè)專(zhuān)門(mén)存放二進(jìn)制數(shù)據(jù)的緩存區(qū)
<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是一個(gè)和二進(jìn)制很像的十六進(jìn)制的字節(jié)碼.兩個(gè)段碼表示一個(gè)字節(jié)
一個(gè)二進(jìn)制表示一個(gè)位
一個(gè)字節(jié)是8個(gè)二進(jìn)制位
一個(gè)16進(jìn)制是2的4次方
// 比如e4
// 16進(jìn)制表示式e4
// 2進(jìn)制表示 11100100
// 11100100 正好是一個(gè)字節(jié)
在內(nèi)存中這個(gè)字節(jié)碼可以直接使用,如果存到硬盤(pán)里,會(huì)自動(dòng)轉(zhuǎn)為二進(jìn)制存儲(chǔ).,這樣轉(zhuǎn)二進(jìn)制會(huì)很方便
2.2. Buffer 創(chuàng)建
Buffer是一個(gè)全局的類(lèi),不需要加載可以直接使用
2.2.1 創(chuàng)建指定長(zhǎng)度的緩存區(qū)
在node.js中默認(rèn)使用utf-8編碼,一個(gè)中文一般占3個(gè)字節(jié)
// 創(chuàng)建10個(gè)字節(jié)的緩存區(qū)
var buf = new Buffer(10)
// Buffer()的寫(xiě)法已經(jīng)棄用, 需要改用Buffer.alloc()
// 寫(xiě)入一個(gè)字符a
buf.write('a')
console.log(buf)
這個(gè)方法已經(jīng)被廢棄,不要在使用了.
2.2.2 指定數(shù)組長(zhǎng)度創(chuàng)建Buffer
var buf = Buffer.alloc(10)
console.log(buf)
沒(méi)什么意義,你知道你要存的內(nèi)容是多大嗎?
2.2.3 使用 from 創(chuàng)建Buffer
var str = 'Hello wrold'
// 將一個(gè)字符串保存到buffer中
var buf = Buffer.from(str)
console.log(buf)
// 注意from 的方法參數(shù)不能是數(shù)字
// 類(lèi)型可以為string, Buffer, ArrayBuffer, Array, or Array-like Object
2.3. 寫(xiě)入緩存區(qū)
buf.write("寫(xiě)入緩存區(qū)")
2.4. 緩存區(qū)復(fù)制
let str = 'Hello wrold'
let buf = Buffer.from(str)
let bff = Buffer.alloc(12)
buf.copy(bff)
2.5. 實(shí)例常用方法
2.5.1 buf.length
獲取buf的長(zhǎng)度
buf.length;
2.5.2 buf.toString()
將buf的內(nèi)容轉(zhuǎn)為字符串
語(yǔ)法
? buf.toString([encoding[,start[,end]]])
參數(shù)
- encoding 可選 , 指定編碼格式 默認(rèn)utf8
- start 可選, 開(kāi)始位置 默認(rèn)最開(kāi)始
- end 可選 結(jié)束位置 默認(rèn)結(jié)束位置
返回值
? 解碼緩沖區(qū)數(shù)據(jù)并使用指定的編碼返回字符串
let buf = Buffer.from('我是誰(shuí)')
// 不傳參
console.log(buf.toString())
// 指定編碼, 以及轉(zhuǎn)換的數(shù)據(jù)
console.log(buf.toString('utf8', 3,6))
2.5.3 buf.fill()
語(yǔ)法
? buf.fill(value [, start, [, end]])
參數(shù)
- value 必須: 初始化數(shù)據(jù)
- start 初始化開(kāi)始的位置
- end 初始化結(jié)束的位置
2.5.4 buf.slice()
用法與字符串的slice用法一樣
let buf = Buffer.from('我是誰(shuí)')
console.log(buf.slice(-6,-3))
2.5.5 buf.copy()
從目標(biāo)buf中拷貝內(nèi)容
語(yǔ)法
? buffer.copy(目標(biāo)buf, 寫(xiě)法buf開(kāi)始的位置, 拷貝buffer開(kāi)始位置, 拷貝buffer結(jié)束位置)
let buf = Buffer.from('我是誰(shuí)')
let buf2 = Buffer.from([0,0,0,0,0])
// 將buf里從下標(biāo)3開(kāi)始,到下標(biāo)6的自己拷貝到 buf2 從buf2第一個(gè)字節(jié)開(kāi)始寫(xiě)入
buf.copy(buf2,1, 3,6)
2.6.類(lèi)常用的方法(靜態(tài)方法)
2.6.1 Buffer.isBuffer()
判斷一個(gè)對(duì)象是不是Buffer對(duì)象
語(yǔ)法
? Buffer.isBuffer(obj)
參數(shù)
? obj 需要判斷的對(duì)象
返回值
? 布爾值
let buf = Buffer.from('我是誰(shuí)')
console.log(Buffer.isBuffer(buf)) // true
2.6.2 Buffer.concat()
語(yǔ)法 Buffer.concat(), 接受一個(gè)數(shù)組參數(shù), 數(shù)組可以說(shuō)是buffer數(shù)組
? Buffer.concat([buf, buf2])
返回值:
? 返回合并后的buffer
let buf = Buffer.from('我')
let buf2 = Buffer.from('是誰(shuí)')
console.log(buf)
console.log(buf2)
let buf3 = Buffer.concat([buf,buf2])
console.log(buf3.toString())
注意點(diǎn)
- Buffer 是用于處理二進(jìn)制數(shù)據(jù)流
- Buffer一個(gè)元素就是一個(gè)字節(jié);
- 實(shí)例類(lèi)似整數(shù)數(shù)組, 但和數(shù)組不一樣的是,一旦實(shí)例,那么大小將固定
- 內(nèi)存不 是由V8分配的, 是C++代碼在V8堆外分配的物理內(nèi)存
2.fs文件模塊(掌握)
由于node.js是服務(wù)器端的程序,必須要有讀寫(xiě)文件操作,在原生JS中沒(méi)有這樣的功能.
在Node中如果要讀寫(xiě)文件,必須使用文件系統(tǒng)模塊(fs),提供了文件操作的所有功能
js在瀏覽器端是不能進(jìn)行I/O操作的,是為了保護(hù)客戶(hù)端的安全, node內(nèi)置的js模塊, 讓js能夠在在node環(huán)境執(zhí)行的時(shí)候, 可以操作服務(wù)器上的資源文件,也就是給與js I/O 操作的能力
2.1. 讀取文件
文件的讀寫(xiě)有兩種方式:
第一種方式是將硬盤(pán)是上的內(nèi)容全部讀入內(nèi)容以后才觸發(fā)回調(diào)函數(shù)
第二種方式 是流式讀取:將數(shù)據(jù)從硬盤(pán)中讀取一節(jié)就觸發(fā)一次回調(diào)函數(shù),也就是讀取一節(jié)數(shù)據(jù)處理一節(jié)數(shù)據(jù)
實(shí)現(xiàn)大文件操作
在直接讀取中又分為兩種,同步讀取和異步讀取
2.1.1 異步方式讀取
fs.rendFile(文件路徑, 回調(diào)函數(shù))
定義一個(gè)回調(diào)函數(shù),接受讀取到的內(nèi)容
// 直接讀取文件 -- 異步
var fs = require('fs');
fs.readFile("./file1.txt", function (err, data) {
console.log(data)
// err 錯(cuò)誤對(duì)象,如果有錯(cuò)則有值,沒(méi)有報(bào)錯(cuò)則為null
// data 是Buffer數(shù)據(jù)流叽奥,如果需要顯示數(shù)據(jù)通過(guò)data.toString
})
// 可以使用特殊的方式,就是第二個(gè)參數(shù)傳入數(shù)據(jù)格式,那么data將直接顯示數(shù)據(jù)
var fs = require('fs');
fs.readFile("./file1.txt", "utf-8",function (err, data) {
console.log(data)
})
實(shí)例:將服務(wù)器上讀取的文件內(nèi)容通過(guò)服務(wù)器返回給瀏覽器
// 創(chuàng)建服務(wù)器用的模塊
const http = require('http');
// 這是讀取文件用模塊
const fs = require('fs')
// 創(chuàng)建一個(gè)服務(wù)器
const server = http.createServer(function (req, res) {
// 設(shè)置響應(yīng)頭
res.writeHead(200, { 'Content-Type': 'text/html;charset=UTF-8' });
// 讀取文本文件,
fs.readFile('./test.txt', function (err, data) {
// 如果數(shù)據(jù)讀取成功,將數(shù)據(jù)返回
res.end(data);
})
})
// 監(jiān)聽(tīng)端口
server.listen(3000);
console.log('服務(wù)器已經(jīng)在3000端口啟動(dòng)了')
?
?
2.1.2 同步讀取文件
同步采用賦值的方式返回?cái)?shù)據(jù)
幾乎所有的fs函數(shù)都有同步版本,只需要在異步版本的函數(shù)后面添加Sync即可
fs.readFileSync(文件路徑)
示例:
// 直接讀取文件 -- 同步
var fs = require('fs');
var data = fs.readFileSync("./file1.txt")
console.log(data.toString());
我們發(fā)現(xiàn)同步版本并沒(méi)有報(bào)錯(cuò)接受,如果同步有錯(cuò),系統(tǒng)將會(huì)自動(dòng)報(bào)錯(cuò)
,所以異步方法,如果有錯(cuò)系統(tǒng)不會(huì)報(bào)錯(cuò),會(huì)將報(bào)錯(cuò)傳給回調(diào)自行處理
2.2. 寫(xiě)入文件
2.2.1 異步版本
語(yǔ)法方式:
fs.writeFile('文件名','數(shù)據(jù)',function(err){ /* 數(shù)據(jù)寫(xiě)入失敗時(shí)的回調(diào)函數(shù)*/ })
示例:
// 寫(xiě)文件 -- 異步
var fs = require('fs');
var data = "<h2>我是通過(guò)fs模塊寫(xiě)入的文件</h2>"
fs.writeFile("wuwei.html", data, function (err) {
console.log(err)
})
// 可以傳入寫(xiě)入寫(xiě)入的字符編碼
fs.writeFile("wuwei.html", data, 'utf8' ,function (err) {
console.log(err)
})
// 如果寫(xiě)入的是buffer ,那么就可以忽略編碼
const content = Buffer.from("this is a test")
fs.writeFile("wuwei.html", content, function (err) {
console.log(err)
})
2.2.2 同步版本
fs.writeFileSync(文件路徑, 數(shù)據(jù))
示例:
var data = "<h2>我是通過(guò)fs模塊寫(xiě)入的文件</h2>"
fs.writeFileSync("wuwei.html", data)
2.3. 追加寫(xiě)入內(nèi)容
2.3.1 異步追加
fs.appendFile(文件路徑, 數(shù)據(jù), 回調(diào)函數(shù))
示例:
var data = "<h2>我是通過(guò)fs模塊寫(xiě)入的文件</h2>"
fs.appendFile("./wuwei.html",data,(err) => {
console.log(err)
})
2.3.2 同步追加
fs.appendFileSync(文件路徑, 數(shù)據(jù))
示例:
var data = "<h2>我是通過(guò)fs模塊寫(xiě)入的文件</h2>"
fs.appendFileSync("./wuwei.txt",data)
2.4. 讀取文件/文件夾信息
2.4.1 語(yǔ)法方式
異步讀取
fs.stat("文件名",function(err,stats){ //state是文件的信息對(duì)象,包含了常用個(gè)文件信息 })
同步讀取
const stat = fs.statSync("./node")
2.4.2 示例
var fs = require("fs");
fs.stat("index.json", function (err, stats) {
console.log(stats)
})
打印stats對(duì)象
Stats {
dev: 226428394,
mode: 33206,
nlink: 1,
uid: 0,
gid: 0,
rdev: 0,
blksize: undefined,
ino: 10414574138374020,
size: 17, //文件大小,字節(jié)數(shù)
blocks: undefined,
atimeMs: 1566988486138.9304, //
mtimeMs: 1566625311439.442,
ctimeMs: 1566625311439.442,
birthtimeMs: 1566625188201.4536,
atime: 2019-08-28T10:34:46.139Z, // 上一次文件訪問(wèn)時(shí)間
mtime: 2019-08-24T05:41:51.439Z, // 文件內(nèi)容修改時(shí)間
ctime: 2019-08-24T05:41:51.439Z, // 文件狀態(tài)改變時(shí)間
birthtime: 2019-08-24T05:39:48.201Z // 第一次創(chuàng)建時(shí)間
}
2.4.3 文件信息對(duì)象的方法
語(yǔ)法
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. 刪除文件
語(yǔ)法:
fs.unlink(path,callback)
// 斷開(kāi)連接式的刪除文件
示例:
fs.unlink("wuwei.html", function (err) {
if (err) {
throw err
} else {
console.log("刪除成功")
}
})
2.6. 新增目錄
語(yǔ)法:
fs.mkdir(path,callback)
示例:
fs.mkdir("./wuwei",function(err){
// 創(chuàng)建失敗的回調(diào)
})
2.7. 讀取目錄中的文件列表
fs.readdir(path,callback)
示例:
fs.readdir("./wuwei",function(err,list){
console.log(err);
console.log(list)
// list 是讀取文件夾列表
})
// 如果文件夾存在的話,list是 一個(gè)node文件夾下所有文件以及文件夾的數(shù)組集合
// 如果文件夾不存在就是err
2.8. 刪除空文件夾
fs.rmdir(path,callback)
示例:
fs.rmdir("./wuwei",function(err){
// err 報(bào)錯(cuò)信息
})
只能刪除空文件夾,不能刪除空文件夾
2.9 監(jiān)聽(tīng)文件是否改變
// watch是監(jiān)聽(tīng) 文件夾內(nèi)所有的文件變化
fs.watch("./",{
recursive: false // 默認(rèn)是flase 是否遞歸監(jiān)聽(tīng)
}, (changeType, filename) => {
console.log(chaneType) // 改變的類(lèi)型 ,是修改一個(gè)文件 還是新增文件亦或是刪除文件
console.log(filename) // 修改的文件名字
})
// watchFile 是監(jiān)聽(tīng)一個(gè)文件的變化
fs.watchFile('./node/1.txt',(c,p) => {
console.log(p);
console.log(c)
})
p
監(jiān)聽(tīng)文件改變之前stats
c
監(jiān)聽(tīng)文件改變之后(即當(dāng)前文件)stats
3.fs流讀寫(xiě)方式(掌握)
用read讀取文件 方法,會(huì)將文件整體讀入緩存區(qū),然后使用,用write方法寫(xiě)入文件的時(shí)候,會(huì)將文件整體讀入緩存區(qū),在修改后整體寫(xiě)入文件,
也就是說(shuō)在node中無(wú)論read還是write都是把文件視為一個(gè)整體.Node需要在內(nèi)存中開(kāi)辟與文件相同大小的緩存空間,如果文件小,的確沒(méi)有什么問(wèn)題,如果是一個(gè)非常大的文件,超過(guò)內(nèi)存大小怎么辦?
3.1. Stream 流介紹
應(yīng)用程序中,流是一種有序的,有起點(diǎn)和終點(diǎn)的字節(jié)數(shù)據(jù)的傳輸方式.在應(yīng)用程序中各種對(duì)象之間交流與傳輸數(shù)據(jù)的的時(shí)候,總是先將該對(duì)象中所包含的數(shù)據(jù)轉(zhuǎn)換為各種形式的流數(shù)據(jù)(即字節(jié)數(shù)據(jù)),在通過(guò)流的傳輸,到達(dá)目的對(duì)象后在將流數(shù)據(jù)轉(zhuǎn)換為該對(duì)象中可以使用的數(shù)據(jù)
所有互聯(lián)網(wǎng)傳輸數(shù)據(jù)都是以流的方式傳輸?shù)?也就是說(shuō)都是一節(jié)一節(jié)的
3.2. 流的操作
3.2.1 流式讀取
語(yǔ)法
// 1. 創(chuàng)建讀取流
var stream = fs.createReadStream(path)
// 2. 綁定data事件接受數(shù)據(jù)
stream.on("data",function(data){
console.log(data)
console.log(1) // 打印幾次,表示數(shù)據(jù)分了幾次流
})
// 3. 綁定end事件表示讀取完畢
stream.on("end",function(){
console.log("數(shù)據(jù)流讀取完畢")
})
// 4. 綁定error錯(cuò)誤事件
stream.on("error",function(err){
throw err
})
示例:
var fs = require("fs");
// 創(chuàng)建一個(gè)可讀流
var stream = fs.createReadStream("./file2.txt");
// 監(jiān)聽(tīng)data事件,每讀一段流數(shù)據(jù)就執(zhí)行一次回調(diào)函數(shù)
stream.on("data", function (data) {
console.log(data)
})
每一節(jié)數(shù)據(jù)流的長(zhǎng)度是65536字節(jié),大小是65536/1024=64kb,也就是每一節(jié)的大小是64kb,可以通過(guò)data.length查看
讀取流事件:end 表示在讀取流讀取完畢后觸發(fā)的事件
stream.on("end", function () {
console.log("讀取完畢")
})
讀取流事件:error 表示在讀取流讀取錯(cuò)誤時(shí)后觸發(fā)的時(shí)間
stream.on("end", function () {
console.log("讀取完畢")
})
3.2.2 以流的方式寫(xiě)文件
語(yǔ)法
// 1. 創(chuàng)建寫(xiě)入流
var stream = fs.createWriteStream(path)
// 2. 寫(xiě)入數(shù)據(jù)
stream.write("數(shù)據(jù)1")
// 3. 寫(xiě)入完畢后結(jié)束寫(xiě)入流
stream.end()
// 4. 數(shù)據(jù)寫(xiě)入完成事件
stream.on("finish",function(){
})
// 5. 綁定error出錯(cuò)事件
stream.on("error",function(err){
})
示例:
var fs = require("fs");
// 創(chuàng)建一個(gè)寫(xiě)入流
var stream = fs.createWriteStream("./file3.txt");
// 寫(xiě)入數(shù)據(jù)
stream.write("面包吃完了");
stream.write("牛奶喝光了");
stream.write("錢(qián)也花完了");
stream.write("工作也丟了");
stream.write("最悲催的是我迷路了");
stream.end(); // 已流的方式寫(xiě)入數(shù)據(jù)必須顯示的聲明結(jié)束
寫(xiě)入流可以通過(guò)監(jiān)聽(tīng)finish事件,來(lái)處理在寫(xiě)入流完成后要做的事情
stream.on("finish", function () {
console.log("寫(xiě)入完成")
})
寫(xiě)入流錯(cuò)誤事件,error,在寫(xiě)入錯(cuò)誤時(shí)觸發(fā)回調(diào)
stream.on("error", function (err) {
console.log(err)
})
3.3. fs管道方式(掌握)
3.3.1. 什么 是管道
管道是Node.Js中流的實(shí)現(xiàn)機(jī)制.
管道(pipe)提供了一個(gè)輸出流到輸入流傳入數(shù)據(jù)的一個(gè)機(jī)制.通常我們用于一個(gè)流中獲取數(shù)據(jù)并將數(shù)據(jù)傳遞到另外一個(gè)流中.
3.3.2. 管道語(yǔ)法
語(yǔ)法
輸出流.pipe(輸入流)
作用
管道可以實(shí)現(xiàn)對(duì)大文件的操作(文件 大小超過(guò)內(nèi)存)
實(shí)例: 不采用管道方式復(fù)制文件
var fs = require("fs");
// 創(chuàng)建讀取流和寫(xiě)入流
var s1 = fs.createReadStream("./file2.txt");
var s2 = fs.createWriteStream("./file3.txt");
// 讀一節(jié)數(shù)據(jù)寫(xiě)入一節(jié)數(shù)據(jù)
s1.on("data", function (d) {
s2.write(d)
})
// 讀入完成后,寫(xiě)入完成
s1.on("end", function () {
s2.end();
console.log("復(fù)制完成")
})
使用管道復(fù)制文件
var fs = require("fs");
// 創(chuàng)建讀取流和寫(xiě)入流
var s1 = fs.createReadStream("./file2.txt");
var s2 = fs.createWriteStream("./file3.txt");
s1.pipe(s2);
3.4. 鏈?zhǔn)搅?/h5>
將多個(gè)管道連接起來(lái),實(shí)現(xiàn)鏈?zhǔn)讲僮?/p>
輸入流.pipe(中轉(zhuǎn)流).pipe(中轉(zhuǎn)流).pipe(...).pipe(輸出流)
例子:使用鏈?zhǔn)搅鲏嚎s文件
var fs = require("fs");
// 引入壓縮模塊
var zlib = require('zlib');
// 創(chuàng)建引入引出流
var s1 = fs.createReadStream("./file3.txt");
var s2 = fs.createWriteStream("./file3.txt.zip");
// 鏈?zhǔn)讲僮?s1.pipe(zlib.createGzip()).pipe(s2)
理解就好 ,后面基本上都是用框架