node模块

node learning

引入events模块

  1. 就一个简单的例子,倒是是如何使用的
  • 引入模块 const EventEmitter = require('events').EventEmitter; 也可以用es6的解构赋值 const {EventEmitter} = require('events');

const EventEmitter = require('events').EventEmitter;

const myEmitter = new EventEmitter();

//用events之前
function fn() {
	console.log('使用普通的回调')
}
setTimeout((fn) => {
	fn();
},3000);

//使用events之后
setTimeout(() => {
	//在这里使用这个事件
	myEmitter.emit('funone');
},3000);

//注册一个事件
myEmitter.on('funone', funOne);

function funOne() {
	console.log('myEmitter的输出');
}

引入url

  1. URL构造函数
    • 获得一个URL对象 new URL('要解析的地址')
    • url的resolve方法 url.resolve('目标地址', '拼接地址')
获取URL对象
	//方法1
	const {URL} = require('url');
	let urlExample = new URL('https://www.shiguangkey.com/video/1599?videoId=22524&classId=2356&playback=1');
	//方法2
	const URL = require('url').URL;
	let urlExample = new URL('https://www.shiguangkey.com/video/1599?videoId=22524&classId=2356&playback=1');
	//方法3  习惯用这个
	const url = require('url');
	let urlExample = new url.URL('https://www.shiguangkey.com/video/1599?videoId=22524&classId=2356&playback=1');
	//urlExample是什么东西,一堆解析出来的东西
	/*
	URL {
	  href: 'https://www.shiguangkey.com/video/1599?videoId=22524&classId=2356&playback=1',
	  origin: 'https://www.shiguangkey.com',
	  protocol: 'https:',
	  username: '',
	  password: '',
	  host: 'www.shiguangkey.com',
	  hostname: 'www.shiguangkey.com',
	  port: '',
	  pathname: '/video/1599',
	  search: '?videoId=22524&classId=2356&playback=1',
	  searchParams: URLSearchParams { 'videoId' => '22524', 'classId' => '2356', 'playback' => '1' },
	  hash: ''
	}
	*/
url模块的resolve
	const url = require('url');
	// aa/bb/cc   当第一个参数不是根目录,并且以/结尾,等于  aa/bb/*   然后第二个参数替代*
	console.log(url.resolve('aa/bb/', 'cc'));
	 // aa/cc     当第一个参数不是根目录,并且不以/结尾,等于  aa/*   然后第二个参数替代*
	console.log(url.resolve('aa/bb', 'cc'));
	 // aa/cc   当第一个参数不是根目录,并且以/结尾,等于  aa/  然后第二个参数以./代表当前目录,
	console.log(url.resolve('aa/bb', './cc'));
	 // cc   当第一个参数不是根目录,并且以/结尾,等于  aa/  然后第二个参数以./代表当前目录,
	console.log(url.resolve('aa/bb', '../cc'));
	 // cc	当第一个参数不是根目录,第二个参数 /开头,代表第二个参数是根目录,所以直接 cc
	console.log(url.resolve('aa/bb', '/cc'));
	 //http://example.com 这一块第一个参数已经是跟目录了,第二参数又根目录,所以第二个无效
	console.log(url.resolve('http://example.com/', '/one'))
	

引入querystring模块 => querystring就是查找字符串,上例子

  • eg:
	//?videoId=22524&classId=2356&playback=1
	//上面就是这个字符串就是请求里面的参数什么的,要把这个变成一个可遍历对象
	const qs = require('querystring');
	//slice主要去掉那个?
	qs.parse('?videoId=22524&classId=2356&playback=1'.slice(1));
	//结果
	/*{
	  videoId: '22524',
	  classId: '2356',
	  playback: '1'
	}
	*/

引入assert模块 => 断言,如果不是我们期望值就会报错,如果是我们期望值则继续执行

  • 直接上例子
const assert = require('assert');
assert.equal(1+2, 3, '第一个参数执行的结果不等于第二个参数报错')
assert.notEqual(1+2, 3, '第一个参数执行的结果等于第二个参数报错s')
assert.deepEqual({}, {}, '第一个参数和第二个参数的属性名和属性值要相等')

引入fs模块(文件系统模块,负责读写)

  1. 异步读取文件(文本文件)
	let fs = require('fs');
	fs.readFile('文件,写好对应的路径', '写好对应的编码', function(err,data) {
		if(err) {
			//打印出错误参数
			console.log(err);
		}else {
			//打印成功的参数
			//成功的时候 err会是一个null值
			console.log(data);
		}
	})
  1. 读书二进制文件(图片呀之类的)
	let fs = require('fs');
	fs.readFile('文件,写好对应的路径', function(err,data) {
		if(err) {
			//打印出错误参数
			console.log(err);
		}else {
			//会返回的是一个Buffer对象
			console.log(data.length);
		}
	})
  1. 同步读取文件
	let fs = require('fs');
	//同步读取发现文件错误,需要用try...catch..来捕获
	try {
		let data = fs.readFileSync('文件路径', 'utf-8');
	}catch (err) {
		console.log(err);
	}
	
	
  1. 异步写入文件
    • 如果传入的数据是String,默认按UTF-8编码写入文本文件,如果传入的参数是Buffer,则写入的是二进制文件
    • 如果文件不存在会自动新建一个
    • 注意每次写入都会覆盖之前的文件里面内容
	let fs = require('fs');
	let data = '我是被写入文件的文字';
	//异步写入文件
	fs.writeFile('./two.txt', data, function(err) {
		if (err) {
			console.log(err);
		}else {
			console.log('success');
		}
	})
  1. 同步写入文件
	let fs = require('fs');
	let data = '我是被写入文件的文字';
	//同步写入文件
	//需要捕获错误就是try catch
	try {
		fs.writeFileSync('文件路径', data);
		console.log('success');
	}catch (err) {
		console.log(err);
	}
  1. stat => 获取文件的大小,创建时间等信息
    • 是否是文件 isFile()
    • 是否是文件夹 isDirectory()
    • 文件大小 size
    • 文件创建时间 birthtime
    • 文件更改时间 mtime
	let fs = require('fs');
	fs.stat('filepath', function(err, stat) {
		 if (err) {
			 console.log(err);
		 }else {
			 console.log('isFile: ' + stat.isFile());
			 console.log(stat.isDirectory());
			 console.log(stat.size);
			 console.log(stat.birthtime);
			 console.log(stat.mtime);
		 }
	})
  1. stat 同步写法
	let fs = require('fs');
	let file = fs.statSync('filePath');
	try {
		//是否一个文件
		console.log(file.isFile());
		//是否一个目录
		console.log(file.isDirectory());
		//大小
		console.log(file.size);
		//创建时间
		console.log(file.birthtime);
		console.log(file.mtime);
	} catch (err) {
		console.log('捕获错误');
	}
  1. 判断文件,文件夹是否存在
	let fs = require('fs');
	fs.existsSync('./xx.txt');
	//结果就会返回false或者true

同步还是异步

* 绝大部分反复执行的逻辑业务代码,异步,不然就会花费大量i/o,失去使用node的最大意义
* 读取配置文件,或者结束时候写入状态,这些开始结束只执行一次,可以使用同步操作

stream => Node.js提供的又一个仅在服务区端可用的模块,目的是支持“流”这种数据结构,流模块抽象,所以都由其他模块继承使用

* 所有读取数据的都继承自 stream.Readable
* 所有写入的流都继承自 stream.Writeable
* 流分成输入流和输出流2种
* 输入流 => 你敲打键盘输入,一个个字符就像输入流
* 输出流 => 程序处理你输入的字符,显示在显示屏里面,这就像输出流
* 在node.js中流也是一个对象,我们只需要响应流的事件就可以了
	* data事件表示流的数据已经可以读取了
	* end事件表示这个流已经到末尾了,没有数据可以读取了
	* error事件表示出错了
  1. 一个最简单的输出流例子
    • 要注意,data事件可能会有多次,每次传递的chunk是流的一部分数据(不是很懂data会有很多次在输出流里面)
	let fs = require('fs');
	//不写字符集也可以单独设置
	//stream.setEncoding('utf-8');
	let stream = fs.createReadStream('./one.txt', 'utf-8');
	
	//chunk 是数据块的意思
	stream.on('data', function(chunk) {
		 console.log('data');
		 console.log(chunk);
	})
	
	stream.on('end', function() {
		 console.log('ending啦');
	})
	
	stream.on('error', function(err) {
		 console.log('报错了');
		 console.log(err);
	})
  1. 用输入流的写入文件,不断的调用write,最后用end结尾
    • 每次end还是会完全覆盖之前的内容
 let fs = require('fs');
 let stream = fs.createWriteStream('./one.txt', 'utf-8');
 
 stream.write('怎么搞的呀\n')
 stream.write('googog\n')

 stream.end();
  1. pipe => 就像可以把两个水管串成一个更长的水管一样,两个流也可以串起来。(用于复制文件)
    • 一个Readable流和一个Writeable流串联起来,数据自动从Readable流进入Writable流,这种操作叫pipe
    • 看一个pipe的例子,eg:
let fs = require('fs');
let rs = fs.createReadStream('文件名字a');
let ws = fs.createWriteStream('文件名字b');
rs.pipe(ws);
//默认情况下,当Readable流的数据读取完毕,end事件触发后,将自动关闭Writable流
//若不想关闭
rs.pipe(ws, {end: false})
  1. stream原生模块使用流
	const Readable = require('stream').Readable;
	const rs = new Readable;
	//往流里面添加东西
	rs.push(1);
	rs.push(2);
	//通过也是继承了stream的process.stdout来输出
	rs.pipe(process.stdout)
	//或者用on事件
	rs.on('data', (chunk) => {
		console.log(chunk);s
	})
	//结果: 12

引入http模块

  • http => 应用程序并不直接和HTTP协议打交道,而是操作http模块提供的request和response对象
    • request对象封装了HTTP请求,我们调用request对象的属性和方法就可以拿到所有HTTP请求的信息
    • response对象封装了HTTP响应,我们操作response对象的方法,就可以把HTTP响应返回给浏览器
  1. 实现一个最简单的web程序,对于所有的请求都返回helloworld
    • request的method就是请求方式 POST or GET
    • request的url就是请求的地址
    • response的writeHead就是把200的成功请求写入响应头
    • response的end就把html内容写入进去,然后就结束
    • server.listen就是监听8888端口
    • response.setHeader(‘Access-Control-Allow-Origin’, ‘*’)设置跨域
//引入http模块
let http = require('http');
//创建一个服务,并传入一个回调函数
let server = http.createServer(function(request, response) {
	//设置cors跨域
	response.setHeader('Access-Control-Allow-Origin', '*');
	console.log('请求方式:' + request.method)
	console.log('请求地址:' + request.url);
	response.writeHead(200, {'Content-Type': 'text/html'});
	//end只能写字符串和buffer
	response.end('<h1>Hello World!</h1>')
	//这样拿到请求头
	JSON.stringify(request.headers);
})

//监听
server.listen(8888);
console.log('Server is running at http://127.0.0.1:8888/');
//监听也有回调函数
server.listen(8888, () => {
	console.log('Server is running at http://127.0.0.1:8888')
})

  1. 代理 => 服务器之间是不存在跨域滴
  • 设置option
  • 设置req 里面要写上data和end事件
  • req的error事件
  • 执行req的write和end方法结尾
			//请求的代理服务器
			const options = {
                hostname: 'localhost',
                port: 8888,
                path: '/heart',
                method: 'POST'
            };
			//发起请求
			const req = http.request(options, res => {
			    res.on('data', chunk => {
			        response.end(chunk);
			    })
			    res.on('end', () => {
			        console.log('炸裂');
			    })
			})
			//写好异常抛出
			req.on('error', error => {
			    console.log('报错啦', error);
			})
			//官网写write的(⊙﹏⊙),我照抄
			req.write('');
			req.end();

文件服务器 => Web程序我们可以设定一个目录,然后让Web程序变成一个文件服务器(组合案例)

* 解析request.url中的路径,通过parse()将一个字符串解析为一个Url对象
* 处理本地文件目录需要使用Node.js提供的path模块
* 用http发起请求
* 最后使用fs输入输出流来把东西返回到前端页面
  1. 解析request.url中的路径

let url = require('url');
//这样就可以打印出解析
console.log(url.parse('http://user:pass@host.com:8080/path/to/file?query=string#hash'));
/*
打印出来的结构
Url {
  protocol: 'http:',
  slashes: true,
  auth: 'user:pass',
  host: 'host.com:8080',
  port: '8080',
  hostname: 'host.com',
  hash: '#hash',
  search: '?query=string',
  query: 'query=string',
  pathname: '/path/to/file',
  path: '/path/to/file?query=string',
  href: 'http://user:pass@host.com:8080/path/to/file?query=string#hash' }
  */
  1. path获取当前目录路径
    为什么要用path?因为path做了兼容,在不同系统下面的拼接是不一样的,node的这个path模块就做了相应的兼容
  • path(这里直接代表引入的path模块)
  • path.resolve(__dirname); path.resolve('.'); 这样都可以获得当前的文件夹路径
  • path.parse(__filename) 解析出当前文件的路径,文件名,扩展名,完整文件名和扩展名
  • eg:
  let path = require('path');
  //当前文件的目录
  let workDir = path.resolve('.');
  //结果:f:\ui-template-library
 
// 拼接文件地址
// 组合完整的文件路径:当前目录+'pub'+'index.html':
  let filePath = path.join(workDir, 'pub', 'index.html');
  console.log(filePath);
  //结果:f:\ui-template-library\pub\index.html
  1. 综合案例(文件服务器)
	let fs = require('fs');
	let http = require('http');
	let path = require('path');
	let url = require('url');
	
	//获取当前目录
	let root = path.resolve('.');
	
	//创建一个服务
	let server = http.createServer(function (request,response) {
		//请求的地址
		let requestUrl = request.url;
		//解析请求的地址获取请求的文件路径
		let pathname = url.parse(requestUrl).pathname;
		//拼接本地根目录路径组成完成的本地绝对路径
		//解释一下为什么不用+拼接字符串因为你拼接会是 f:\ui-template-library + /node-note.md
		let filepath = path.join(root,pathname);
		//获取文件信息 => 判断拿到的是不是一个文件
		fs.stat(filepath, function (err, stat) {
			if (stat.isFile() && !err){
				//响应200表示读取成功
				response.writeHead(200);
				//用pipe把读取的文件响应回去前端
				//解释一下,这里不再创建输出流是因为response对象本身就是一个Writable Stream
				fs.createReadStream(filepath).pipe(response);
			} else {
				//出错了或者文件不存在
				console.log('404' + request.url);
				//响应404
				response.writeHead(404);
				response.end('404 NOT FOUND')
				
			}
		})
		
	})
	
	//监听8080端口
	server.listen(8080);
	console.log('Server is running at http://127.0.0.1:8080/');
  1. 综合案例联系补充 => 在浏览器输入http://localhost:8080/时,会返回404,原因是程序识别出HTTP请求的不是文件,而是目录。请修改file_server.js,如果遇到请求的路径是目录,则自动在目录下依次搜索index.html、default.html,如果找到了,就返回HTML文件的内容。

crypto => crypto模块的目的是为了提供通用的加密和哈希算法

1.MD5和SHA1
	//引入模块
	let crypto = require('crypto');
	//用模块创建一个md5的哈希对象
	let md5 = crypto.createHash('md5');
	//使用md5加密
	md5.update('is ok');
	//可以多次调用update
	md5.update('no problem');
	//计算所有需要被哈希化的数据摘要,可以指定编码,这里用hex
	//如果要用utf-8 是携程cp.digest('utf8')
	//这里输出的是加密的结果
	console.log(md5.digest('hex'));
  • 如果要使用sha1 就把createHash(‘这里的值修改就对了’)
  • 还有更加安全的 sha256和sha512
2.Hmac => Hmac算法也是一种哈希算法,它可以利用MD5或SHA1等哈希算法。不同的是,Hmac还需要一个密钥

疑问:这个密钥有什么鬼用

	//引入模块
	let crypto = require('crypto');
	//用模块创建一个md5的哈希对象
	let hmac = crypto.createHash('sha256', 'secret-key');
	
	hmac.update('is ok');
	hmac.update('no problem');
	console.log(hmac.digest('hex'));
  • 只要密钥发生了变化,那么同样的输入数据也会得到不同的签名,因此,可以把Hmac理解为用随机数“增强”的哈希算法。
3.AES => AES是一种常用的对称加密算法,加解密都用同一个密钥,crypto模块提供了AES支持,但是需要自己封装好函数
  • 给个例子,暂时不深究

const crypto = require('crypto');

//加密方法
function aesEncrypt(data, key) {
    const cipher = crypto.createCipher('aes192', key);
    var crypted = cipher.update(data, 'utf8', 'hex');
    crypted += cipher.final('hex');
    return crypted;
}

//解密方法
function aesDecrypt(encrypted, key) {
    const decipher = crypto.createDecipher('aes192', key);
    var decrypted = decipher.update(encrypted, 'hex', 'utf8');
    decrypted += decipher.final('utf8');
    return decrypted;
}

var data = 'Hello, this is a secret message!';
var key = 'Password!';
var encrypted = aesEncrypt(data, key);
var decrypted = aesDecrypt(encrypted, key);

console.log('Plain text: ' + data);
console.log('Encrypted text: ' + encrypted);
console.log('Decrypted text: ' + decrypted);

//结果:
//Encrypted text: 8a944d97bdabc157a5b7a40cb180e713f901d2eb454220d6aaa1984831e17231f87799ef334e3825123658c80e0e5d0c
//Decrypted text: Hello, this is a secret message!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值