碼文不易啊,轉載請帶上本文鏈接呀,感謝感謝 https://www.cnblogs.com/echoyya/p/14473101.html
Buffer (緩沖區)
JavaScript 語言自身只有字符串數據類型,沒有二進制數據類型。二進制可以存儲任意類型的數據,電腦中所有的數據都是二進制。
在處理像TCP流或文件流時,必須使用到二進制數據。因此在 Node.js中,定義了一個 Buffer 類,該類用來創建一個專門存放二進制數據的緩存區。
Buffer 與字符編碼:當在 Buffer
和字符串之間轉換時,可以指定字符編碼。 如果未指定字符編碼,則默認使用 UTF-8 。
Buffer 創建
Buffer對象可以通過多種方式創建,v6.0之前直接使用new Buffer()構造函數來創建對象實例,v6.0以后,官方文檔建議使用Buffer.from() 創建對象。nodejs中文網、菜鳥教程-nodejs
Buffer.from(buffer)
:復制傳入的 Buffer ,返回一個新的 Buffer 實例
Buffer.from(string[, encoding])
:要編碼的字符串。字符編碼。默認值: 'utf8'
。
Buffer.alloc(size[, fill[, encoding]])
: 返回一個指定大小的 Buffer 實例,如果沒有設置 fill,則默認填滿 0
var buf = Buffer.from('Echoyya');
var buf1 = Buffer.from(buf);
console.log(buf); // <Buffer 45 63 68 6f 79 79 61>
buf1[0] = 0x65;
console.log(buf.toString());// Echoyya
console.log(buf1.toString()); // echoyya
var buf2 = Buffer.from('4563686f797961', 'hex'); // 設置編碼
console.log(buf2); // <Buffer 45 63 68 6f 79 79 61>
console.log(buf2.toString()); // Echoyya
var buf3 = Buffer.from('4563686f797961'); // 默認編碼
console.log(buf3); // <Buffer 34 35 36 33 36 38 36 66 37 39 37 39 36 31>
console.log(buf3.toString()); // 4563686f797961
var buf4 = Buffer.alloc(4);
console.log(buf4); // <Buffer 00 00 00 00>
Buffer 寫入
buf.write(string[, offset[, length]][, encoding])
參數描述:
-
string - 寫入緩沖區的字符串。
-
offset - 緩沖區開始寫入的索引值,默認為 0 。
-
length - 寫入的字節數,默認為 buffer.length
-
encoding - 使用的編碼。默認為 'utf8' 。
根據 encoding 的字符編碼寫入 string 到 buf 中的 offset 位置。 length 參數是寫入的字節數。
返回值:返回實際寫入的大小。如果 buffer 空間不足, 則只會寫入部分字符串。
//buffer的大小一旦被確定則不能被修改
var buf5 = Buffer.alloc(4);
console.log(buf5.length); // 4
var len = buf5.write("Echoyya");
console.log(buf5.toString()); // Echo
console.log("寫入字節數 : "+ len); // 寫入字節數 : 4
Buffer 讀取
讀取 Node 緩沖區數據:buf.toString([encoding[, start[, end]]])
參數描述:
-
encoding - 使用的編碼。默認為 'utf8' 。
-
start - 指定開始讀取的索引位置,默認為 0。
-
end - 結束位置,默認為緩沖區的末尾。
返回值:解碼緩沖區數據並使用指定的編碼返回字符串。
buf = Buffer.alloc(26);
for (var i = 0 ; i < 26 ; i++) {
buf[i] = i + 97;
}
console.log(buf); // <Buffer 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a>
console.log( buf.toString('ascii')); // abcdefghijklmnopqrstuvwxyz
console.log( buf.toString('ascii',0,5)); // abcde
console.log( buf.toString('utf8',0,5)); // abcde
console.log( buf.toString(undefined,0,5)); // abcde 默認utf8
更多>>
除上述最基本的讀寫操作外,還有許多強大的API:
-
緩沖區合並:Buffer.concat(list[, totalLength])
-
緩沖區比較:Buffer.compare(target[, targetStart[, targetEnd[, sourceStart[, sourceEnd]]]])
-
緩沖區判斷:Buffer.isBuffer(obj)
-
緩沖區拷貝:Buffer.copy(target[, targetStart[, sourceStart[, sourceEnd]]])
fs (文件系統)
Node.js提供一組文件操作API,fs
模塊可用於與文件系統進行交互。所有的文件系統操作都具有同步的、回調的、以及基於 promise 的形式。同步與異步的區別,主要在於有無回調函數,同步方法直接在異步方法后面加上Sync
const fs = require('fs');
讀取文件
異步:fs.readFile(path, callback)
同步:fs.readFileSync(path)
fs.readFile('文件名', (err, data) => {
if (err) throw err;
console.log(data);
});
var data = fs.readFileSync('文件名');
獲取文件信息
異步模式獲取文件信息:fs.stat(path, callback)
- path - 文件路徑。
- callback - 回調函數,帶有兩個參數如:(err, stats), stats 是 fs.Stats 對象。
fs.stat(path)執行后,將stats實例返回給回調函數。可以通過提供方法判斷文件的相關屬性。
var fs = require('fs');
fs.stat('./data', function (err, stats) {
// stats 是文件的信息對象,包含常用的文件信息
// size: 文件大小(字節)
// mtime: 文件修改時間
// birthtime:文件創建時間
// 等等...
console.log(stats.isDirectory()); //true
})
stats類中的方法有:
方法 | 描述 |
---|---|
stats.isFile() | 判斷是文件返回 true,否則返回 false。 |
stats.isDirectory() | 判斷是目錄返回 true,否則返回 false。 |
var fs = require("fs");
console.log("准備打開文件!");
fs.stat('./data', function (err, stats) {
if (err) {
return console.error(err);
}
console.log(stats);
console.log("讀取文件信息成功!");
// 檢測文件類型
console.log("是否為文件(isFile) ? " + stats.isFile());
console.log("是否為目錄(isDirectory) ? " + stats.isDirectory());
});
寫入文件
異步:fs.writeFile(file, data, callback)
同步:fs.writeFileSync(file, data)
,沒有返回值
如果文件存在,該方法寫入的內容會覆蓋原有內容,反之文件不存在,調用該方法寫入將創建一個新文件
const fs = require('fs')
var hello = '<h1>hello fs</h1>'
fs.writeFile('./index.html',hello,function(err){
if(err) throw err
else console.log('文件寫入成功');
})
var helloSync= '<h1>hello fs Sync</h1>'
fs.writeFileSync('./index.html',helloSync)
刪除文件
異步:fs.unlink(path, callback)
同步:fs.unlinkSync(path)
,沒有返回值
對空或非空的目錄均不起作用。 若要刪除目錄,則使用 fs.rmdir()。
const fs = require('fs')
fs.unlink('./index.html',function(err){
if(err) throw err
else console.log('文件刪除成功');
})
fs.unlinkSync('./index.html')
目錄操作
-
創建目錄
-
異步:
fs.mkdir(path, callback)
-
同步:
fs.mkdirSync(path)
,沒有返回值
-
-
讀取目錄文件
-
異步:
fs.readdir(path, callback)
,callback 回調有兩個參數err, files,files是目錄下的文件列表。 -
同步:
fs.readdirSync(path)
-
const fs = require("fs");
console.log("查看 ./data 目錄");
fs.readdir("./data",function(err, files){
if(err) throw err
else{
files.forEach( function (file){
console.log( file );
});
}
});
var files = fs.readdirSync('./data')
-
刪除空目錄
注:不能刪除非空目錄
-
異步:
fs.rmdir(path, callback)
,回調函數,沒有參數 -
同步:
fs.rmdirSync(path)
-
-
刪除非空目錄(遞歸)
實現思路:
-
fs.readdirSync:讀取文件夾中所有文件及文件夾
-
fs.statSync:讀取每一個文件的詳細信息
-
stats.isFile():判斷是否是文件,是文件則刪除,否則遞歸調用自身
-
fs.rmdirSync:刪除空文件夾
-
const fs = require('fs')
function deldir(p) {
// 讀取文件夾中所有文件及文件夾
var list = fs.readdirSync(p)
list.forEach((v, i) => {
// 拼接路徑
var url = p + '/' + v
// 讀取文件信息
var stats = fs.statSync(url)
// 判斷是文件還是文件夾
if (stats.isFile()) {
// 當前為文件,則刪除文件
fs.unlinkSync(url)
} else {
// 當前為文件夾,則遞歸調用自身
arguments.callee(url)
}
})
// 刪除空文件夾
fs.rmdirSync(p)
}
deldir('./data')
Stream (流)
流
是一組有序的、有起點、有終點的字節數據的傳輸方式,在應用程序中,各種對象之間交換與傳輸數據時:
-
總是先將該對象總所包含的數據轉換為各種形式的流數據(即字節數據)
-
在流傳輸到達目的對象后,再將流數據轉換為該對象中可以使用的數據
與直接讀寫文件的區別:可以監聽它的'data',一節一節處理文件,用過的部分會被GC(垃圾回收),所以占內存少。 readFile是把整個文件全部讀到內存里。然后再寫入文件,對於小型的文本文件,沒多大問題,但對於體積較大文件,使用這種方法,很容易使內存“爆倉”。理想的方法應該是讀一節寫一節
流的類型:
-
Readable - 可讀操作。
-
Writable - 可寫操作。
-
Duplex - 可讀可寫操作.
-
Transform - 操作被寫入數據,然后讀出結果。
常用的事件:
-
data - 當有數據可讀時觸發。
-
end - 沒有更多的數據可讀時觸發。
-
error - 在接收和寫入過程中發生錯誤時觸發。
-
finish - 所有數據已被寫入到底層系統時觸發。
讀取流
var fs = require('fs')
var data = '';
// 創建可讀流
var readerStream = fs.createReadStream('./file.txt');
// 設置編碼為 utf8。
readerStream.setEncoding('UTF8');
// 處理流事件 --> data, end, error
readerStream.on('data', function(chunk) {
data += chunk;
console.log(chunk.length) // 一節65536字節, 65536/1024 = 64kb
});
readerStream.on('end',function(){
console.log(data);
});
readerStream.on('error', function(err){
console.log(err.stack);
});
console.log("程序執行完畢");
寫入流
var fs = require('fs')
var data = '創建一個可以寫入的流,寫入到文件 file1.txt 中';
// 創建寫入流,文件 file1.txt
var writerStream = fs.createWriteStream('./file1.txt')
// 使用 utf8 編碼寫入數據
writerStream.write(data,'UTF8');
// 標記文件末尾
writerStream.end();
// 處理流事件 --> finish、error
writerStream.on('finish', function() {
console.log("寫入完成。");
});
writerStream.on('error', function(err){
console.log(err.stack);
});
console.log("程序執行完畢");
管道 pipe
管道提供了一個輸出流 -> 輸入流的機制。通常用於從一個流中獲取數據傳遞到另外一個流中。用一根管子(pipe)連接兩個桶使得水從一個桶流入另一個桶,這樣就可實現大文件的復制過程。
管道語法:reader.pipe(writer);
讀取 input.txt 文件內容,並寫入 output.txt 文件,兩種實現方式對比:
var fs = require("fs");
var readerStream = fs.createReadStream('input.txt'); // 創建一個可讀流
var writerStream = fs.createWriteStream('output.txt'); // 創建一個可寫流
//1. 以流的方式實現大文件復制
readerStream.on('data',function(chunk){
writerStream.write(chunk) // 讀一節寫一節
})
readerStream.on('end',function(){ // 無可讀數據
writerStream.end() // 標記文件末尾
writerStream.on('finish',function(){ // 所有數據已被寫入
console.log('復制完成')
})
})
// 2. 以管道方式實現大文件復制
readerStream.pipe(writerStream);
鏈式流
將多個管道連接起來,實現鏈式操作
用管道和鏈式來壓縮和解壓文件:
var fs = require("fs");
var zlib = require('zlib');
// 壓縮 input.txt 文件為 input.txt.gz
var reader = fs.createReadStream('input.txt')
var writer = fs.createWriteStream('input.txt.gz')
reader.pipe(zlib.createGzip()).pipe(writer);
// 解壓 input.txt.gz 文件為 input.txt
var reader = fs.createReadStream('input.txt.gz')
var writer = fs.createWriteStream('input.txt')
reader.pipe(zlib.createGunzip()).pipe(writer);
path (路徑)
是nodejs中提供的系統模塊,不需安裝,用於格式化或拼接轉換路徑,執行效果會因應用程序運行所在的操作系統不同,而有所差異。
常用方法:
方法 | 描述 |
---|---|
path.normalize(path) | 規范化給定的 path ,解析 '..' 和 '.' 片段。 |
path.join([...paths]) | 將所有給定的 path 片段連接到一起(使用平台特定的分隔符作為定界符),然后規范化生成的路徑。如果路徑片段不是字符串,則拋出 TypeError 。 |
path.dirname(path) | 返回路徑中文件夾 部分 |
path.basename(path) | 返回路徑中文件 部分(文件名和擴展名) |
path.extname(path) | 返回路徑中擴展名 部分 |
path.parse(path) | 解析路徑,返回一個對象,其屬性表示 path 的有效元素 |
var path = require('path')
var p1 = "../../../hello/../a/./b/../c.html"
var p2 = path.normalize(p1)
console.log(path.normalize(p1)); // ../../../a/c.html
console.log(path.dirname(p2)) // ../../../a
console.log(path.basename(p2)) // c.html
console.log(path.extname(p2)) // .html
console.log(path.parse(p2)) // { root: '', dir: '..\\..\\..\\a', base: 'c.html', ext: '.html', name: 'c'}
console.log(path.join('/目錄1', '目錄2', '目錄3/目錄4', '目錄5')); // '/目錄1/目錄2/目錄3/目錄4'
var pArr = ['/目錄1', '目錄2', '目錄3/目錄4', '目錄5']
console.log(path.join(...pArr)); // '/目錄1/目錄2/目錄3/目錄4'
// path.join('目錄1', {}, '目錄2'); // 拋出 ' The "path" argument must be of type string. Received an instance of Object'
url (URL)
url :全球統一資源定位符
,對網站資源的一種簡潔表達形式,也稱為網址
官方規定完整構成:協議://用戶名:密碼@主機名.名.域:端口號/目錄名/文件名.擴展名?參數名=參數值&參數名2=參數值2#hash哈希地址
http 協議 URL常見結構:協議://主機名.名.域/目錄名/文件名.擴展名?參數名=參數值&參數名2=參數值2#hash哈希地址
域名是指向IP地址的,需要解析到 IP 地址上,服務器與IP地址形成標識,域名只是人可以看懂的符號,而計算機並看不懂,所以需要 DNS 域名服務器來解析。
nodejs中提供了兩套對url模塊進行處理的API功能,二者處理后的結果有所不同:
-
舊版本傳統的 API
-
實現了 WHATWG 標准的新 API
var url = require('url');
var uu = 'https://music.163.com:80/aaa/index.html?id=10#/discover/playlist'
// WHATWG 標准API 解析 URL 字符串
var wUrl = new url.URL(uu);
console.log(wUrl);
// 使用傳統的 API 解析 URL 字符串:
var tUrl = url.parse(uu);
console.log(tUrl);
http (協議)
網絡是信息傳輸、接收、共享的虛擬平台,而網絡傳輸數據有一定的規則,稱協議,HTTP就是其中之一,且使用最為頻繁。
B/S開發模式
(Browser/Server,瀏覽器/服務器模式),瀏覽器(web客戶端)使用HTTP協議就可以訪問web服務器上的數據
客戶端:發送請求,等待響應
服務器:處理請求,返回響應
定義、約束、交互特點
定義:HTTP 即 超文本傳輸協議
,是一種網絡傳輸協議,采用的是請求 / 響應
方式傳遞數據,該協議規定了數據在服務器與瀏覽器之間,傳輸數據的格式與過程
約束:
-
約束了瀏覽器以何種格式兩服務器發送數據
-
約束了服務器以何種格式接收客戶端發送的數據
-
約束了服務器以何種格式響應數據給瀏覽器
-
約束了以何種格式接收服務器響應的數據
交互特點:一次請求對應一次響應,多次請求對應多次響應
http (模塊)
http模塊是nodejs中系統模塊,用於網絡通訊,可以作為客戶端發送請求
,亦可以作為服務器端處理響應
,
限於篇幅,另總結了一篇博文,介紹了兩者的用法,以及客戶端向服務器端傳參,詳見:NodeJs 入門到放棄 — 網絡服務器(三)
網絡爬蟲
網絡爬蟲(又稱為網頁蜘蛛,網絡機器人),是一種按照一定的規則,自動地抓取萬維網信息的程序或者腳本。
小案例
需求:寫一個爬蟲程序批量下載圖片 http://www.nipic.com/photo/canyin/xican/index.html
思路:
-
打開對應網站查看內容,找圖片地址,找規律
-
編寫代碼獲取網站內html代碼
-
通過正則表達式提取出圖片地址
-
遍歷圖片地址數組,請求數據
-
將獲取到的圖片保存下來
var http = require('http')
var fs = require('fs')
var path = require('path')
http.get('http://www.jituwang.com/tuku/index.html',function(res){
var data = '' // 用於存放一節一節的HTML數據
// 以流的方式讀取數據
res.on('data',function(chunk){
data += chunk.toString()
})
res.on('end',function(){
// 正則獲取所有圖片地址
var reg = /<img src="(.+?)" alt=".+?"\/>/ig
var result = ''
var imgArr = []
while(result = reg.exec(data)){
imgArr.push(result[1])
}
// 根據數組中的圖片地址 獲取圖片數據
for (var i in imgArr) {
setTimeout(function(i){
getImg(imgArr[i])
},1000*i,i)
}
// fs.writeFileSync('./b.txt',imgArr) // 可寫入文件,查看圖片地址
})
})
function getImg(url){
http.get(url,function(res){
res.pipe(fs.createWriteStream(path.join('./img',path.basename(url))))
})
}
for循環請求數據時,為避免對其服務器造成壓力,設置定時器,每隔一秒請求一次,將讀到的數據,存儲為與服務器上同名。利用上述path模塊提供的方法,path.basename(url)
上集: NodeJs 入門到放棄 — 入門基本介紹(一)