|
|
<
七天教会NodeJS(两)文件操纵(fs、buffer、stream(防爆仓)、path、遍历目次)、收集操纵(http、URL)
文章目次
总结:
- 文件操纵
- 小文件拷贝
- fs.writeFileSync(dst, fs.readFileSync(src));
复造代码
- 年夜文件拷贝
- fs.createReadStream(src).pipe(fs.createWriteStream(dst));
复造代码
- agrv
- process是一个齐局变量,可经由过程process.argv得到号令止参数。
- 因为argv[0]牢固即是NodeJS施行法式的尽对途径,argv[1]牢固即是主模块的尽对途径,因而第一个号令止参数从argv[2]那个地位开端。
- Buffer
- JS言语本身只要字符串数据规范,出有两进造数据规范,因而NodeJS供给了一个取String对等的齐局机关函数Buffer去供给对两进造数据的操纵。除能够读与文件获得Buffer的真破例,借可以间接机关,比方:
- var bin = new Buffer([ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]);
复造代码 - Buffer取字符串相同,除能够用.length属性获得字节少度中,借能够用[index]方法读与指定地位的字节
- Buffer取字符串可以互相转化,比方可使用指定编码将两进造数据转化为字符串
- Buffer取字符串有一个主要区分。字符串是只读的,而且对字符串的任何修正获得的皆是一个新字符串,本字符串连结不变。至于Buffer,更像是能够做指针操纵的C言语数组。比方,能够用[index]方法间接修正某个地位的字节.
- stream(防爆仓)
- 当内乱存中没法一次拆下需求处置的数据时,大要一边读与一边处置愈加下效时,我们便需求用到数据流。NodeJS中经由过程各类Stream去供给对数据流的操纵。
- Stream基于变乱机造事情,一切Stream的真例皆担当于NodeJS供给的EventEmitter。
- 假如写进速率跟没有上读与速率的话,只写数据流内乱部的缓存会爆仓。我们能够按照.write办法的返回值去判定传进的数据是写进目的了,仍是暂时放正在了缓存了,并按照drain变乱去判定甚么时分只写数据流曾经将缓存中的数据写进目的,能够传进下一个待写数据了。
- fs文件体系
- NodeJS最精华的同步IO模子正在fs模块里有着充分的表现
- 文件属性读写。
此中经常使用的有fs.stat、fs.chmod、fs.chown等等。
- 文件内乱容读写。
此中经常使用的有fs.readFile、fs.readdir、fs.writeFile、fs.mkdir等等。
- 底层文件操纵。
此中经常使用的有fs.open、fs.read、fs.write、fs.close等等。
- 把握好目次遍历
- fs.readdir(dir, function (err, files)
复造代码- var pathname = path.join(dir, files[i]);
复造代码- fs.stat(pathname, function (err, stats)
复造代码
- 收集操纵
- 两种利用方法
- 做为效劳端利用时,创立一个HTTP效劳器,监听HTTP客户端恳求并返反响应。
- var http = require('http');
- http.createServer(function (request, response) {
- response.writeHead(200, { 'Content-Type': 'text-plain' });
- response.end('Hello World\n');
- }).listen(8124);
复造代码 - 做为客户端利用时,倡议一个HTTP客户端恳求,获得效劳端呼应。
- 客户端形式
- .request办法创立了一个客户端,并指定恳求目的战恳求头数据。
- 以后,就能够把request工具看成一个只写数据流去写进恳求体数据战结束恳求
- HTTP恳求素质上是一个数据流,由恳求头(headers)战恳求体(body)构成。
- HTTP呼应素质上也是一个数据流,一样由呼应头(headers)战呼应体(body)构成。
- https
- https模块取http模块极其相同,区分正在于https模块需求分外处置SSL证书。
- URL
- .parse办法去将一个URL字符串转换为URL工具:url.parse()
- .parse办法借撑持第两个战第三个布我规范可选参数。
- 第两个参数即是true时,该办法返回的URL工具中,query字段没有再是一个字符串,而是一个颠末querystring模块转换后的参数工具。
- 第三个参数即是true时,该办法能够准确剖析没有带和谈头的URL,比方//www.example.com/foo/bar。
- format办法许可将一个URL工具转换为URL字符串
- .resolve办法能够用于拼接URL
1. 文件操纵
让前端觉得如获神器的没有是NodeJS能做收集编程,而是NodeJS可以操纵文件。小至文件查找,年夜至代码编译,险些出有一个前端东西没有操纵文件。换个角度讲,险些也只需求一些数据处置逻辑,再减上一些文件操纵,就可以够编写出年夜大都前端东西。本章将介绍取之相干的NodeJS内乱置模块。
开门白
NodeJS供给了根本的文件操纵API,可是像文件拷贝这类初级功用便出有供给,因而我们先拿文件拷贝法式练脚。取copy号令相同,我们的法式需求能承受源文件途径取目的文件途径两个参数。
小文件拷贝
我们利用NodeJS内乱置的fs模块简朴完成那个法式以下。
- var fs = require('fs');
- function copy(src, dst) {
- fs.writeFileSync(dst, fs.readFileSync(src));
- }
- function main(argv) {
- copy(argv[0], argv[1]);
- }
- main(process.argv.slice(2));
复造代码 以上法式利用fs.readFileSync从源途径读与文件内乱容,并利用fs.writeFileSync将文件内乱容写进目的途径。
豆常识: process是一个齐局变量,可经由过程process.argv得到号令止参数。因为argv[0]牢固即是NodeJS施行法式的尽对途径,argv[1]牢固即是主模块的尽对途径,因而第一个号令止参数从argv[2]那个地位开端。
年夜文件拷贝
上边的法式拷贝一些小文件出啥成绩,但这类一次性把一切文件内乱容皆读与到内乱存中后再一次性写进磁盘的方法没有合适拷贝年夜文件,内乱存会爆仓。关于年夜文件,我们只能读一面写一面,曲到完成拷贝。因而上边的法式需求革新以下。
- var fs = require('fs');
- function copy(src, dst) {
- fs.createReadStream(src).pipe(fs.createWriteStream(dst));
- }
- function main(argv) {
- copy(argv[0], argv[1]);
- }
- main(process.argv.slice(2));
复造代码 以上法式利用fs.createReadStream创立了一个源文件的只读数据流,并利用fs.createWriteStream创立了一个目的文件的只写数据流,而且用pipe办法把两个数据留连接了起去。毗连起去后发作的工作,道得笼统面的话,火逆着火管从一个桶流到了另外一个桶。
API走马不雅花
我们先大抵看看NodeJS供给了哪些战文件操纵有闭的API。那里其实不一一介绍每一个API的利用办法,民圆文档曾经做得很好了。
Buffer(数据块)
民圆文档: http://nodejs.org/api/buffer.html
JS言语本身只要字符串数据规范,出有两进造数据规范,因而NodeJS供给了一个取String对等的齐局机关函数Buffer去供给对两进造数据的操纵。除能够读与文件获得Buffer的真破例,借可以间接机关,比方:
- var bin = new Buffer([ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]);
复造代码 Buffer取字符串相同,除能够用.length属性获得字节少度中,借能够用[index]方法读与指定地位的字节,比方:
Buffer取字符串可以互相转化,比方可使用指定编码将两进造数据转化为字符串:
- var str = bin.toString('utf-8'); // => "hello"
复造代码 大要反过去,将字符串转换为指定编码下的两进造数据:
- var bin = new Buffer('hello', 'utf-8'); // => <Buffer 68 65 6c 6c 6f>
复造代码 Buffer取字符串有一个主要区分。字符串是只读的,而且对字符串的任何修正获得的皆是一个新字符串,本字符串连结不变。至于Buffer,更像是能够做指针操纵的C言语数组。比方,能够用[index]方法间接修正某个地位的字节。
而.slice办法也没有是返回一个新的Buffer,而更像是返回了指背本Buffer中心的某个地位的指针,以下所示。
- [ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]
- ^ ^
- | |
- bin bin.slice(2)
复造代码 因而对.slice办法返回的Buffer的修正会感化于本Buffer,比方:
- var bin = new Buffer([ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]);
- var sub = bin.slice(2);
- sub[0] = 0x65;
- console.log(bin); // => <Buffer 68 65 65 6c 6f>
复造代码 也因而,假如念要拷贝一份Buffer,得起首创立一个新的Buffer,并经由过程.copy办法把本Buffer中的数据复造已往。那个相同于申请一块新的内乱存,并把已有内乱存中的数据复造已往。以下是一个例子。
- var bin = new Buffer([ 0x68, 0x65, 0x6c, 0x6c, 0x6f ]);
- var dup = new Buffer(bin.length);
- bin.copy(dup);
- dup[0] = 0x48;
- console.log(bin); // => <Buffer 68 65 6c 6c 6f>
- console.log(dup); // => <Buffer 48 65 65 6c 6f>
复造代码 总之,Buffer将JS的数据处置才能从字符串扩大到了随便两进造数据。
Stream(数据流)
民圆文档: http://nodejs.org/api/stream.html
当内乱存中没法一次拆下需求处置的数据时,大要一边读与一边处置愈加下效时,我们便需求用到数据流。NodeJS中经由过程各类Stream去供给对数据流的操纵。
以上边的年夜文件拷贝法式为例,我们能够为数据根源创立一个只读数据流,示比方下:
- var rs = fs.createReadStream(pathname);
- rs.on('data', function (chunk) {
- doSomething(chunk);
- });
- rs.on('end', function () {
- cleanUp();
- });
复造代码 豆常识: Stream基于变乱机造事情,一切Stream的真例皆担当于NodeJS供给的EventEmitter。
上边的代码中data变乱会络绎不绝天被触收,不论doSomething函数能否处置得过去。代码能够持续做以下革新,以处理那个成绩。
- var rs = fs.createReadStream(src);
- rs.on('data', function (chunk) {
- rs.pause();
- doSomething(chunk, function () {
- rs.resume();
- });
- });
- rs.on('end', function () {
- cleanUp();
- });
复造代码 以上代码给doSomething函数减上了回调,因而我们能够正在处置数据前停息数据读与,并正在处置数据后持续读与数据。
别的,我们也能够为数据目的创立一个只写数据流,示比方下:
- var rs = fs.createReadStream(src);
- var ws = fs.createWriteStream(dst);
- rs.on('data', function (chunk) {
- ws.write(chunk);
- });
- rs.on('end', function () {
- ws.end();
- });
复造代码 我们把doSomething换成了往只写数据流里写进数据后,以上代码看起去便像是一个文件拷贝法式了。可是以上代码存正在上边提到的成绩,假如写进速率跟没有上读与速率的话,只写数据流内乱部的缓存会爆仓。我们能够按照.write办法的返回值去判定传进的数据是写进目的了,仍是暂时放正在了缓存了,并按照drain变乱去判定甚么时分只写数据流曾经将缓存中的数据写进目的,能够传进下一个待写数据了。因而代码能够革新以下:
- var rs = fs.createReadStream(src);
- var ws = fs.createWriteStream(dst);
- rs.on('data', function (chunk) {
- if (ws.write(chunk) === false) {
- rs.pause();
- }
- });
- rs.on('end', function () {
- ws.end();
- });
- ws.on('drain', function () {
- rs.resume();
- });
复造代码 以上代码完成了数据从只读数据流到只写数据流的搬运,并包罗了防爆仓掌握。由于这类利用场景许多,比方上边的年夜文件拷贝法式,NodeJS间接供给了.pipe办法去做那件工作,其内乱部完成方法取上边的代码相同。
File System(文件体系)
民圆文档: http://nodejs.org/api/fs.html
NodeJS经由过程fs内乱置模块供给对文件的操纵。fs模块供给的API根本上能够分为以下三类:
- 文件属性读写。
此中经常使用的有fs.stat、fs.chmod、fs.chown等等。
- 文件内乱容读写。
此中经常使用的有fs.readFile、fs.readdir、fs.writeFile、fs.mkdir等等。
- 底层文件操纵。
此中经常使用的有fs.open、fs.read、fs.write、fs.close等等。
NodeJS最精华的同步IO模子正在fs模块里有着充分的表现,比方上边提到的那些API皆经由过程回调函数传递成果。以fs.readFile为例:
- fs.readFile(pathname, function (err, data) {
- if (err) {
- // Deal with error.
- } else {
- // Deal with data.
- }
- });
复造代码 如上边代码所示,根本上一切fs模块API的回调参数皆有两个。第一个参数正在有毛病发作时即是非常工具,第两个参数一直用于返回API办法施行成果。
别的,fs模块的一切同步API皆有对应的同步版本,用于没法利用同步操纵时,大要同步操纵更便利时的状况。同步API除办法名的开端多了一个Sync以外,非常工具取施行成果的传递方法也有响应变革。一样以fs.readFileSync为例:
- try {
- var data = fs.readFileSync(pathname);
- // Deal with data.
- } catch (err) {
- // Deal with error.
- }
复造代码 fs模块供给的API许多,那里纷歧一介绍,需求时请自止查阅民圆文档。
Path(途径)
民圆文档: http://nodejs.org/api/path.html
操纵文件时不免没有取文件途径挨交讲。NodeJS供给了path内乱置模块去简化途径相干操纵,并提拔代码可读性。以下别离介绍几个经常使用的API。
- path.normalize
将传进的途径转换为尺度途径,具体讲的话,除了解析途径中的.取..中,借能来失落过剩的斜杠。假如有法式需求利用途径做为某些数据的索引,但又许可用户随便输进途径时,便需求利用该办法包管途径的独一性。以下是一个例子:
- var cache = {};
- function store(key, value) {
- cache[path.normalize(key)] = value;
- }
- store('foo/bar', 1);
- store('foo//baz//../bar', 2);
- console.log(cache); // => { "foo/bar": 2 }
复造代码 坑出出留意: 尺度化以后的途径里的斜杠正在Windows体系下是\,而正在Linux体系下是/。假如念包管任何体系下皆利用/做为途径分开符的话,需求用.replace(/\\/g, '/')再交换一下尺度途径。
- path.join
将传进的多个途径拼接为尺度途径。该办法可避免脚工拼接途径字符串的烦琐,而且能正在不同体系下准确利用响应的途径分开符。以下是一个例子:
- path.join('foo/', 'baz/', '../bar'); // => "foo/bar"
复造代码 - path.extname
当我们需求按照不同文件扩大名做不同操纵时,该办法便隐得很好用。以下是一个例子:
- path.extname('foo/bar.js'); // => ".js"
复造代码 path模块供给的其他办法也未几,略微看一下民圆文档就可以局部把握。
遍历目次
遍历目次是操纵文件时的一个常睹需供。好比写一个法式,需求找到并处置指定目次下的一切JS文件时,便需求遍历全部目次。
递回算法
遍历目次时普通利用递回算法,不然便易以编写出烦琐的代码。递回算法取数教归结法相同,经由过程不竭减少成绩的范围去处理成绩。以下示例阐明了这类办法。
- function factorial(n) {
- if (n === 1) {
- return 1;
- } else {
- return n * factorial(n - 1);
- }
- }
复造代码 上边的函数用于计较N的阶乘(N!)。能够看到,当N年夜于1时,成绩简化为计较N乘以N-1的阶乘。当N即是1时,成绩抵达最小范围,没有需求再简化,因而间接返回1。
圈套: 利用递回算法编写的代码固然烦琐,但因为每递回一次便发生一次函数挪用,正在需求劣先考虑机能时,需求把递回算法转换为轮回算法,以裁减函数挪用次数。
遍历算法
目次是一个树状构造,正在遍用时普通利用深度劣先+先序遍历算法。深度劣先,意味着抵达一个节面后,起首接着遍历子节面而没有是邻居节面。先序遍历,意味着初次抵达了某节面便算遍历完成,而没有是最初一次返回某节面才算数。因而利用这类遍历方法时,下边那棵树的遍历挨次是A > B > D > E > C > F。
同步遍历
了解了需要的算法后,我们能够简朴天完成以下目次遍历函数。
- function travel(dir, callback) {
- fs.readdirSync(dir).forEach(function (file) {
- var pathname = path.join(dir, file);
- if (fs.statSync(pathname).isDirectory()) {
- travel(pathname, callback);
- } else {
- callback(pathname);
- }
- });
- }
复造代码 能够看到,该函数以某个目次做为遍历的出发点。碰到一个子目次时,便先接着遍历子目次。碰到一个文件时,便把文件的尽对途径传给回调函数。回调函数拿到文件途径后,就能够做各类判定战处置。因而假定有以下目次:
- - /home/user/
- - foo/
- x.js
- - bar/
- y.js
- z.css
复造代码 利用以下代码遍历该目次时,获得的输进以下。
- travel('/home/user', function (pathname) {
- console.log(pathname);
- });
- ------------------------
- /home/user/foo/x.js
- /home/user/bar/y.js
- /home/user/z.css
复造代码 同步遍历
假如读与目次或读与文件形态时利用的是同步API,目次遍历函数完成起去会有些庞大,但道理完整不异。travel函数的同步版本以下。
- function travel(dir, callback, finish) {
- fs.readdir(dir, function (err, files) {
- (function next(i) {
- if (i < files.length) {
- var pathname = path.join(dir, files[i]);
- fs.stat(pathname, function (err, stats) {
- if (stats.isDirectory()) {
- travel(pathname, callback, function () {
- next(i + 1);
- });
- } else {
- callback(pathname, function () {
- next(i + 1);
- });
- }
- });
- } else {
- finish && finish();
- }
- }(0));
- });
- }
复造代码 p那里没有具体介绍同步遍历函数的编写本领,正在后绝章节中会具体介绍那个。总之我们能够看到同步编程仍是蛮庞大的。/p h3a id="_512"/a文本编码/h3 p利用NodeJS编写前端东西时,操纵得最多的是文本文件,因而也便触及到了文件编码的处置成绩。我们经常使用的文本编码有codeUTF8/code战codeGBK/code两种,而且codeUTF8/code文件借大要带有BOM。正在读与不同编码的文本文件时,需求将文件内乱容转换为JS利用的codeUTF8/code编码字符串后才华一般处置。/p h4a id="BOM_516"/aBOM的移除/h4 pBOM用于标记一个文本文件利用Unicode编码,其自己是一个Unicode字符("\uFEFF"),位于文本文件头部。正在不同的Unicode编码下,BOM字符对应的两进造字节以下:/p- Bytes Encoding
- ----------------------------
- FE FF UTF16BE
- FF FE UTF16LE
- EF BB BF UTF8
复造代码 p因而,我们能够按照文本文件头几个字节即是啥去判定文件能否包罗BOM,和利用哪一种Unicode编码。可是,BOM字符固然起到了标记文件编码的感化,其自己却没有属于文件内乱容的一部门,假如读与文本文件时没有来失落BOM,正在某些利用场景下便会有成绩。比方我们把几个JS文件兼并成一个文件后,假如文件中心露有BOM字符,便会招致阅读器JS语法毛病。因而,利用NodeJS读与文本文件时,普通需求来失落BOM。比方,以下代码完成了辨认战来除UTF8 BOM的功用。/p- function readText(pathname) {
- var bin = fs.readFileSync(pathname);
- if (bin[0] === 0xEF && bin[1] === 0xBB && bin[2] === 0xBF) {
- bin = bin.slice(3);
- }
- return bin.toString('utf-8');
- }
复造代码 h4a id="GBKUTF8_542"/aGBK转UTF8/h4 pNodeJS撑持正在读与文本文件时,大要正在codeBuffer/code转换为字符串时指定文本编码,但遗憾的是,GBK编码没有正在NodeJS本身撑持范畴内乱。因而,普通我们借助codeiconv-lite/code那个三圆包去转换编码。利用NPM下载该包后,我们能够按下边方法编写一个读与GBK文本文件的函数。/p- var iconv = require('iconv-lite');
- function readGBKText(pathname) {
- var bin = fs.readFileSync(pathname);
- return iconv.decode(bin, 'gbk');
- }
复造代码 h4a id="_556"/a单字节编码/h4 p偶然候,我们没法预知需求读与的文件采取哪一种编码,因而也便没法指定准确的编码。好比我们要处置的某些CSS文件中,有的用GBK编码,有的用UTF8编码。固然能够必然水平能够按照文件的字节内乱容推测出文本编码,但那里要介绍的是有些范畴,可是要简朴很多的一种手艺。/p p起首我们明白,假如一个文本文件只包罗英笔墨符,好比codeHello World/code,那不管用GBK编码或是UTF8编码读与那个文件皆是出成绩的。那是由于正在那些编码下,ASCII0~128范畴内乱字符皆利用不异的单字节编码。/p p反过来说,即便一个文本文件中有中文等字符,假如我们需求处置的字符仅正在ASCII0~128范畴内乱,好比除正文战字符串之外的JS代码,我们就能够同一利用单字节编码去读与文件,不消体贴文件的实践编码是GBK仍是UTF8。以下示例阐明了这类办法。/p- 1. GBK编码源文件内乱容:
- var foo = '中文';
- 2. 对应字节:
- 76 61 72 20 66 6F 6F 20 3D 20 27 D6 D0 CE C4 27 3B
- 3. 利用单字节编码读与后获得的内乱容:
- var foo = '{治码}{治码}{治码}{治码}';
- 4. 交换内乱容:
- var bar = '{治码}{治码}{治码}{治码}';
- 5. 利用单字节编码保留后对应字节:
- 76 61 72 20 62 61 72 20 3D 20 27 D6 D0 CE C4 27 3B
- 6. 利用GBK编码读与后获得内乱容:
- var bar = '中文';
复造代码 p那里的窍门正在于,不论年夜于0xEF的单个字节正在单字节编码下被剖析成甚么治码字符,利用一样的单字节编码保留那些治码字符时,背后对应的字节连结不变。/p pNodeJS中自带了一种codebinary/code编码能够用去完成那个办法,因而鄙人例中,我们利用这类编码去演示上例对应的代码该怎样写。/p- function replace(pathname) {
- var str = fs.readFileSync(pathname, 'binary');
- str = str.replace('foo', 'bar');
- fs.writeFileSync(pathname, str, 'binary');
- }
复造代码 h3a id="_591"/a小结/h3 p本章介绍了利用NodeJS操纵文件时需求的API和一些本领,总结起去有以下几面:/p ulli教好文件操纵,编写各类法式皆没有怕。/lili假如没有是很在乎机能,codefs/code模块的同步API能让糊口愈加漂亮。/lili需求对文件读写做到字节级此外精密掌握时,请利用codefs/code模块的文件底层操纵API。/lili没有要利用拼接字符串的方法去处置途径,利用codepath/code模块。/lili把握好目次遍历战文件编码处置本领,很适用。/li/ul h2a id="2__603"/a2. 收集操纵/h2 p没有了解收集编程的法式员没有是好前端,而NodeJS刚好供给了一扇了解收集编程的窗心。经由过程NodeJS,除能够编写一些效劳端法式去辅佐前端开拓战测试中,借可以进修一些HTTP和谈取Socket和谈的相干常识,那些常识正在劣化前端机能战排查前端毛病时道没有定能派上用处。本章将介绍取之相干的NodeJS内乱置模块。/p h3a id="_607"/a开门白/h3 pNodeJS原来的用处是编写下机能Web效劳器。我们起首正在那里反复一下民圆文档里的例子,利用NodeJS内乱置的codehttp/code模块简朴完成一个HTTP效劳器。/p- var http = require('http');
- http.createServer(function (request, response) {
- response.writeHead(200, { 'Content-Type': 'text-plain' });
- response.end('Hello World\n');
- }).listen(8124);
复造代码 pmark以上法式创立了一个HTTP效劳器并监听code8124/code端心,翻开阅读器会见该端心codehttp://127.0.0.1:8124//code就可以够看到结果/mark。/p- const http = require('http')
- const port = 3000
- const server = http.createServer((req, res) => {
- res.statusCode = 200
- res.setHeader('Content-Type', 'text/plain')
- res.end('您好天下\n')
- })
- server.listen(port, () => {
- console.log(`效劳器运转正在 http://${hostname}:${port}/`)
- })
复造代码 豆常识: 正在Linux体系下,监听1024以下端心需求root权限。因而,假如念监听80或443端心的话,需求利用sudo号令启动法式。
API走马不雅花
我们先大抵看看NodeJS供给了哪些战收集操纵有闭的API。那里其实不一一介绍每一个API的利用办法,民圆文档曾经做得很好了。
HTTP
民圆文档: http://nodejs.org/api/http.html
'http’模块供给两种利用方法:
- 做为效劳端利用时,创立一个HTTP效劳器,监听HTTP客户端恳求并返反响应。
- 做为客户端利用时,倡议一个HTTP客户端恳求,获得效劳端呼应。
起首我们去看看效劳端形式下怎样事情。如开门白中的例子所示,起首需求利用.createServer办法创立一个效劳器,然后挪用.listen办法监听端心。以后,每当去了一个客户端恳求,创立效劳器时传进的回调函数便被挪用一次。能够看出,那是一种变乱机造。
HTTP恳求素质上是一个数据流,由恳求头(headers)战恳求体(body)构成。比方以下是一个完好的HTTP恳求数据内乱容。
- POST / HTTP/1.1
- User-Agent: curl/7.26.0
- Host: localhost
- Accept: */*
- Content-Length: 11
- Content-Type: application/x-www-form-urlencoded
- Hello World
复造代码 能够看到,空止之上是恳求头,之下是恳求体。HTTP恳求正在收收给效劳器时,能够以为是根据从头至尾的挨次一个字节一个字节天以数据流方法收收的。而http模块创立的HTTP效劳器正在领受到完好的恳求头后,便会挪用回调函数。正在回调函数中,除可使用request工具会见恳求头数据中,借能把request工具看成一个只读数据流去会见恳求体数据。以下是一个例子。
- http.createServer(function (request, response) {
- var body = [];
- console.log(request.method);
- console.log(request.headers);
- request.on('data', function (chunk) {
- body.push(chunk);
- });
- request.on('end', function () {
- body = Buffer.concat(body);
- console.log(body.toString());
- });
- }).listen(80);
- ------------------------------------
- POST
- { 'user-agent': 'curl/7.26.0',
- host: 'localhost',
- accept: '*/*',
- 'content-length': '11',
- 'content-type': 'application/x-www-form-urlencoded' }
- Hello World
复造代码 HTTP呼应素质上也是一个数据流,一样由呼应头(headers)战呼应体(body)构成。比方以下是一个完好的HTTP恳求数据内乱容。
- HTTP/1.1 200 OK
- Content-Type: text/plain
- Content-Length: 11
- Date: Tue, 05 Nov 2013 05:31:38 GMT
- Connection: keep-alive
- Hello World
复造代码 正在回调函数中,除可使用response工具去写进呼应头数据中,借能把response工具看成一个只写数据流去写进呼应体数据。比方正在以下例子中,效劳端本样将客户端恳求的恳求体数据返回给客户端。
- http.createServer(function (request, response) {
- response.writeHead(200, { 'Content-Type': 'text/plain' });
- request.on('data', function (chunk) {
- response.write(chunk);
- });
- request.on('end', function () {
- response.end();
- });
- }).listen(80);
复造代码 接下去我们看看客户端形式下怎样事情。为了倡议一个客户端HTTP恳求,我们需求指定目的效劳器的地位并收收恳求头战恳求体,以下示例演示了具体做法。
- var options = {
- hostname: 'www.example.com',
- port: 80,
- path: '/upload',
- method: 'POST',
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded'
- }
- };
- var request = http.request(options, function (response) {});
- request.write('Hello World');
- request.end();
复造代码 能够看到,.request办法创立了一个客户端,并指定恳求目的战恳求头数据。以后,就能够把request工具看成一个只写数据流去写进恳求体数据战结束恳求。别的,因为HTTP恳求中GET恳求是最多见的一种,而且没有需求恳求体,因而http模块也供给了以下便利API。
- http.get('http://www.example.com/', function (response) {});
复造代码 当客户端收收恳求并领受到完好的效劳端呼应头时,便会挪用回调函数。正在回调函数中,除可使用response工具会见呼应头数据中,借能把response工具看成一个只读数据流去会见呼应体数据。以下是一个例子。
- http.get('http://www.example.com/', function (response) {
- var body = [];
- console.log(response.statusCode);
- console.log(response.headers);
- response.on('data', function (chunk) {
- body.push(chunk);
- });
- response.on('end', function () {
- body = Buffer.concat(body);
- console.log(body.toString());
- });
- });
- ------------------------------------
- 200
- { 'content-type': 'text/html',
- server: 'Apache',
- 'content-length': '801',
- date: 'Tue, 05 Nov 2013 06:08:41 GMT',
- connection: 'keep-alive' }
- <!DOCTYPE html>
- ...
复造代码 HTTPS
民圆文档: http://nodejs.org/api/https.html
https模块取http模块极其相同,区分正在于https模块需求分外处置SSL证书。
正在效劳端形式下,创立一个HTTPS效劳器的示比方下。
- var options = {
- key: fs.readFileSync('./ssl/default.key'),
- cert: fs.readFileSync('./ssl/default.cer')
- };
- var server = https.createServer(options, function (request, response) {
- // ...
- });
复造代码 能够看到,取创立HTTP效劳器比拟,多了一个options工具,经由过程key战cert字段指定了HTTPS效劳器利用的公钥战公钥。
别的,NodeJS撑持SNI手艺,能够按照HTTPS客户端恳求利用的域名静态利用不同的证书,因而统一个HTTPS效劳器可使用多个域名供给效劳。接着上例,可使用以下办法为HTTPS效劳器增加多组证书。
- server.addContext('foo.com', {
- key: fs.readFileSync('./ssl/foo.com.key'),
- cert: fs.readFileSync('./ssl/foo.com.cer')
- });
- server.addContext('bar.com', {
- key: fs.readFileSync('./ssl/bar.com.key'),
- cert: fs.readFileSync('./ssl/bar.com.cer')
- });
复造代码 正在客户端形式下,倡议一个HTTPS客户端恳求取http模块险些不异,示比方下。
- var options = {
- hostname: 'www.example.com',
- port: 443,
- path: '/',
- method: 'GET'
- };
- var request = https.request(options, function (response) {});
- request.end();
复造代码 但假如目的效劳器利用的SSL证书是便宜的,没有是从颁布机构购置的,默许状况下https模块会回绝毗连,提醒道有证书宁静成绩。正在options里参与rejectUnauthorized: false字段能够禁用对质书有用性的查抄,从而许可https模块恳求开拓情况下利用便宜证书的HTTPS效劳器。
URL
民圆文档: http://nodejs.org/api/url.html
处置HTTP恳求时url模块利用率超下,由于该模块许可剖析URL、天生URL,和拼接URL。起首我们去看看一个完好的URL的各构成部门。
- href
- -----------------------------------------------------------------
- host path
- --------------- ----------------------------
- http: // user:pass @ host.com : 8080 /p/a/t/h ?query=string #hash
- ----- --------- -------- ---- -------- ------------- -----
- protocol auth hostname port pathname search hash
- ------------
- query
复造代码 我们可使用.parse办法去将一个URL字符串转换为URL工具,示比方下。
- url.parse('http://user:pass@host.com:8080/p/a/t/h?query=string#hash');
- /* =>
- { protocol: 'http:',
- auth: 'user:pass',
- host: 'host.com:8080',
- port: '8080',
- hostname: 'host.com',
- hash: '#hash',
- search: '?query=string',
- query: 'query=string',
- pathname: '/p/a/t/h',
- path: '/p/a/t/h?query=string',
- href: 'http://user:pass@host.com:8080/p/a/t/h?query=string#hash' }
- */
复造代码 传给.parse办法的纷歧定如果一个完好的URL,比方正在HTTP效劳器回调函数中,request.url没有包罗和谈头战域名,但一样能够用.parse办法剖析。
- http.createServer(function (request, response) {
- var tmp = request.url; // => "/foo/bar?a=b"
- url.parse(tmp);
- /* =>
- { protocol: null,
- slashes: null,
- auth: null,
- host: null,
- port: null,
- hostname: null,
- hash: null,
- search: '?a=b',
- query: 'a=b',
- pathname: '/foo/bar',
- path: '/foo/bar?a=b',
- href: '/foo/bar?a=b' }
- */
- }).listen(80);
复造代码 .parse办法借撑持第两个战第三个布我规范可选参数。第两个参数即是true时,该办法返回的URL工具中,query字段没有再是一个字符串,而是一个颠末querystring模块转换后的参数工具。第三个参数即是true时,该办法能够准确剖析没有带和谈头的URL,比方//www.example.com/foo/bar。
反过去,format办法许可将一个URL工具转换为URL字符串,示比方下。
- url.format({
- protocol: 'http:',
- host: 'www.example.com',
- pathname: '/p/a/t/h',
- search: 'query=string'
- });
- /* =>
- 'http://www.example.com/p/a/t/h?query=string'
- */
复造代码 别的,.resolve办法能够用于拼接URL,示比方下。
- url.resolve('http://www.example.com/foo/bar', '../baz');
- /* =>
- http://www.example.com/baz
- */
复造代码 Query String
民圆文档: http://nodejs.org/api/querystring.html
querystring模块用于完成URL参数字符串取参数工具的互相转换,示比方下。
- querystring.parse('foo=bar&baz=qux&baz=quux&corge');
- /* =>
- { foo: 'bar', baz: ['qux', 'quux'], corge: '' }
- */
- querystring.stringify({ foo: 'bar', baz: ['qux', 'quux'], corge: '' });
- /* =>
- 'foo=bar&baz=qux&baz=quux&corge='
- */
复造代码 Zlib
民圆文档: http://nodejs.org/api/zlib.html
zlib模块供给了数据紧缩和解压的功用。当我们处置HTTP恳求战呼应时,大要需求用到那个模块。
起首我们看一个利用zlib模块紧缩HTTP呼应体数据的例子。那个例子中,判定了客户端能否撑持gzip,并正在撑持的状况下利用zlib模块返回gzip以后的呼应体数据。
- http.createServer(function (request, response) {
- var i = 1024,
- data = '';
- while (i--) {
- data += '.';
- }
- if ((request.headers['accept-encoding'] || '').indexOf('gzip') !== -1) {
- zlib.gzip(data, function (err, data) {
- response.writeHead(200, {
- 'Content-Type': 'text/plain',
- 'Content-Encoding': 'gzip'
- });
- response.end(data);
- });
- } else {
- response.writeHead(200, {
- 'Content-Type': 'text/plain'
- });
- response.end(data);
- }
- }).listen(80);
复造代码 接着我们看一个利用zlib模块解压HTTP呼应体数据的例子。那个例子中,判定了效劳端呼应能否利用gzip紧缩,并正在紧缩的状况下利用zlib模块解压呼应体数据。
- var options = {
- hostname: 'www.example.com',
- port: 80,
- path: '/',
- method: 'GET',
- headers: {
- 'Accept-Encoding': 'gzip, deflate'
- }
- };
- http.request(options, function (response) {
- var body = [];
- response.on('data', function (chunk) {
- body.push(chunk);
- });
- response.on('end', function () {
- body = Buffer.concat(body);
- if (response.headers['content-encoding'] === 'gzip') {
- zlib.gunzip(body, function (err, data) {
- console.log(data.toString());
- });
- } else {
- console.log(data.toString());
- }
- });
- }).end();
复造代码 Net
民圆文档: http://nodejs.org/api/net.html
net模块可用于创立Socket效劳器或Socket客户端。因为Socket正在前端范围的利用范畴借没有是很广,那里先没有触及到WebSocket的介绍,仅仅简朴演示一下怎样从Socket层里去完成HTTP恳求战呼应。
起首我们去看一个利用Socket拆建一个很没有松散的HTTP效劳器的例子。那个HTTP效劳器不论支到啥恳求,皆牢固返回不异的呼应。
- net.createServer(function (conn) {
- conn.on('data', function (data) {
- conn.write([
- 'HTTP/1.1 200 OK',
- 'Content-Type: text/plain',
- 'Content-Length: 11',
- '',
- 'Hello World'
- ].join('\n'));
- });
- }).listen(80);
复造代码 接着我们去看一个利用Socket倡议HTTP客户端恳求的例子。那个例子中,Socket客户端正在成立毗连后收收了一个HTTP GET恳求,并经由过程data变乱监听函数去获得效劳器呼应。
- var options = {
- port: 80,
- host: 'www.example.com'
- };
- var client = net.connect(options, function () {
- client.write([
- 'GET / HTTP/1.1',
- 'User-Agent: curl/7.26.0',
- 'Host: www.百度.com',
- 'Accept: */*',
- '',
- ''
- ].join('\n'));
- });
- client.on('data', function (data) {
- console.log(data.toString());
- client.end();
- });
复造代码 灵机一面
利用NodeJS操纵收集,出格是操纵HTTP恳求战呼应时会碰到一些欣喜,那里对一些常睹成绩做解问。
- 问: 为何经由过程headers工具会见到的HTTP恳求头或呼应头字段没有是驼峰的?
问: 从标准上讲,HTTP恳求头战呼应头字段皆该当是驼峰的。但理想是残暴的,没有是每一个HTTP效劳端或客户端法式皆严厉遵照标准,以是NodeJS正在处置从此外客户端或效劳端支到的头字段时,皆同一天转换为了小写字母格局,以便开拓者能利用同一的方法去会见头字段,比方headers['content-length']。
- 问: 为何http模块创立的HTTP效劳器返回的呼应是chunked传输方法的?
问: 由于默许状况下,利用.writeHead办法写进呼应头后,许可利用.write办法写进随便少度的呼应体数据,并利用.end办法结束一个呼应。因为呼应体数据少度没有肯定,因而NodeJS主动正在呼应头里增加了Transfer-Encoding: chunked字段,并采取chunked传输方法。可是当呼应体数据少度肯定时,可以使用.writeHead办法正在呼应头里减上Content-Length字段,如许做以后NodeJS便没有会主动增加Transfer-Encoding字段战利用chunked传输方法。
- 问: 为何利用http模块倡议HTTP客户端恳求时,偶然候会发作socket hang up毛病?
问: 倡议客户端HTTP恳求前需求先创立一个客户端。http模块供给了一个齐局客户端http.globalAgent,可让我们利用.request或.get办法时不消脚动创立客户端。可是齐局客户端默许只许可5个并收Socket毗连,当某一个时辰HTTP客户端恳求创立过量,超越那个数字时,便会发作socket hang up毛病。处理办法也很简朴,经由过程http.globalAgent.maxSockets属性把那个数字改年夜些便可。别的,https模块碰到那个成绩时也一样经由过程https.globalAgent.maxSockets属性去处置。
小结
本章介绍了利用NodeJS操纵收集时需求的API和一些坑躲避本领,总结起去有以下几面:
- http战https模块撑持效劳端形式战客户端形式两种利用方法。
- request战response工具除用于读写头数据中,皆能够看成数据流去操纵。
- url.parse办法减上request.url属性是处置HTTP恳求时的牢固拆配。
- 利用zlib模块能够裁减利用HTTP和谈时的数据传输量。
- 经由过程net模块的Socket效劳器取客户端可对HTTP和谈做底层操纵。
- 当心踩坑。
免责声明:假如进犯了您的权益,请联络站少,我们会实时删除侵权内乱容,感谢协作! |
1、本网站属于个人的非赢利性网站,转载的文章遵循原作者的版权声明,如果原文没有版权声明,按照目前互联网开放的原则,我们将在不通知作者的情况下,转载文章;如果原文明确注明“禁止转载”,我们一定不会转载。如果我们转载的文章不符合作者的版权声明或者作者不想让我们转载您的文章的话,请您发送邮箱:Cdnjson@163.com提供相关证明,我们将积极配合您!
2、本网站转载文章仅为传播更多信息之目的,凡在本网站出现的信息,均仅供参考。本网站将尽力确保所提供信息的准确性及可靠性,但不保证信息的正确性和完整性,且不对因信息的不正确或遗漏导致的任何损失或损害承担责任。
3、任何透过本网站网页而链接及得到的资讯、产品及服务,本网站概不负责,亦不负任何法律责任。
4、本网站所刊发、转载的文章,其版权均归原作者所有,如其他媒体、网站或个人从本网下载使用,请在转载有关文章时务必尊重该文章的著作权,保留本网注明的“稿件来源”,并自负版权等法律责任。
|