nodeJs中常用的一些内置模块有:
new URL(urlString,base)
,如果 urlString 是相对路径,则需要 base。 如果 urlString 是绝对路径,则忽略 base。它的部分属性如下:
const myURL = new URL('https://example.org:8088/abc/foo#bar');
console.log(myURL.hash); //#bar
console.log(myURL.hostname); //example.org
console.log(myURL.pathname); //abc/foo#bar
const searchURL = new URL('https://example.org/abc?123');
console.log(searchURL.search); //?123
searchURL.search = 'abc=xyz';
console.log(searchURL.href); //https://example.org/abc?abc=xyz
在http请求中,可以使用new URL()来获取想要的数据:
request.url
;request.headers.host
;let url=new URL("/user?name=haha&age=18", "http://localhost:8088");
//"/user?name=haha&age=18"相当于request.url
//"http://localhost:8088"相当于request.headers.host
console.log(url);
解析传进来的路径。
语法:url.parse(urlString, parseQueryString, slashesDenoteHost)
;
将传入的url对象,拼接成Url路径字符串;
const url = require('url');
var params = {
protocol: 'https:',
hostname: 'www.lagou.com',
port: '8080',
pathname: '/a',
search: '?name=zhangsan&age=20',
hash: '#position',
}
console.log(url.format(params));
//https://www.lagou.com:8080/a?name=zhangsan&age=20#position
拼接url路径,按照第一个参数的目录路径来拼接,类似于path的resolve();
const url = require('url');
url.resolve('/one/two/three', 'four');
// '/one/two/four'
url.resolve('http://example.com/', '/one');
// 'http://example.com/one'
url.resolve('http://example.com/one', '/two');
// 'http://example.com/two'
querystring.escape(str)
,对给定的 str 执行 URL 百分比编码,通常不会直接使用,直接使用querystring.stringify();querystring.unescape(str)
,对给定的 str 上执行 URL 百分比编码字符的解码,通常不会直接使用,直接使用querystring.parse();默认使用 JavaScript 内置的 decodeURIComponent() 方法进行解码;const qs = require('querystring');
var strUrl = 'https://www.baidu.com/s?key=前端&date=0317';
var tmp = qs.escape(strUrl);
console.log(tmp);
//https%3A%2F%2Fwww.baidu.com%2Fs%3Fkey%3D%E5%89%8D%E7%AB%AF%26date%3D0317
console.log(qs.unescape(tmp));
//https://www.baidu.com/s?key=前端&date=0317
querystring.parse(str, sep, eq, options)
,将 URL 查询字符串 str 解析为键值对的集合。
const qs = require('querystring');
var myUrl=new URL('https://www.baidu.com/s?key=前端&date=0317');
var searchStr=myUrl.search.substring(1);
console.log(qs.parse(searchStr));
//{ key: '前端', date: '0317' }
querystring.stringify(obj, sep, eq, options)
,将传入的对象拼接成字符串。
const qs = require('querystring');
var params = {
name: 'wangh',
age: 20
}
console.log(qs.stringify(params));
//name=wangh&age=20
console.log(qs.stringify(params, '#', '^'));
//name^wangh#age^20
由于大多数请求都是没有主体的 GET 请求,因此 Node.js 提供了这个便捷的方法。 这个方法与 http.request() 的唯一区别是它将方法设置为 GET 并自动调用 req.end()。
http.get(url,callback)
res.resume();
来释放内存。const http=require("http");
http.get('http://localhost:3000', (res) => {
res; //服务端返回的响应数据
res.statusCode; //响应的状态码
res.headers; //响应的消息头对象。
if(请求失败) res.resume();
}
http.request(url, options, callback)
。req.end()
来表示请求的结束,const options = {
hostname: 'localhost',
port: 80,
path: '/upload',
method: 'POST',//GET 或者 POST,大写
headers: {//请求头信息}
};
const clientReq = http.request(options, (res) => {
res; //服务端返回的响应数据
});
//请求遇到问题
clientReq.on('error', (e) => {
console.error(`请求遇到问题: ${e.message}`);
});
//发送请求
clientReq.write(postData);
//请求结束
clientReq.end();
创建服务,http.createServer()
返回新的 http.Server 实例。
const http = require('http')
var app = http.createServer((request, response) => {
request.method; //判断是GET请求还是POST请求
request.url; //路径端口号后面的内容
request.header; //请求头内容
request.on("data",()=>{}); //收到客户端发来的数据
request.on("end",()=>{ //数据接收完毕
response.setHeader(); //设置响应头内容
response.write(str); //发送数据给客户端
response.end(); //响应完毕
});
})
app.listen(3000, () => { //开启服务
console.log('localtion start 3000')
})
response.setHeader(name, value)
单个响应头的值
。 如果此响应头已存在于待发送的响应头中,则其值将被替换。response.writeHead(statusCode, headers)
必须在 response.end() 之前调用
。当使用 response.setHeader() 设置响应头时,它们将与传给 response.writeHead() 的任何响应头合并,其中 response.writeHead() 的响应头优先
。
response.getHeader() 只能获取到 setHeader() 的内容。
// 返回 content-type = text/plain
const server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'text/html');
res.setHeader('X-Foo', 'bar');
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('ok');
});
用于处理文件路径和目录路径。
该方法返回 path 的目录名,类似于 Unix 的 dirname 命令。
path.dirname('/foo/bar/baz/asdf/quux');
// 返回: '/foo/bar/baz/asdf'
该方法返回 path 的扩展名。
path.extname('index.html');
// 返回: '.html'
path.extname('.index.md');
// 返回: '.md'
该方法返回一个对象,其属性表示 path 的重要元素。 尾部的目录分隔符将被忽略。
path.parse('/home/user/dir/file.txt');
// 返回:
// { root: '/',
// dir: '/home/user/dir',
// base: 'file.txt',
// ext: '.txt',
// name: 'file' }
path.resolve('/foo/bar', './baz');
// 返回: '/foo/bar/baz'
path.resolve('/foo/bar', '../baz');
// 返回: '/foo/baz'
path.resolve('/foo/bar', '/tmp/file/');
// 返回: '/tmp/file'
eventEmitter.emit()
,用于抛发/派发事件。eventEmitter.on()
,用于侦听事件,可以传任意数量的参数到监听器函数。eventEmitter.once()
,表示事件只被侦听一次。 当事件被触发时,监听器会被注销。const eventEmitter = require('events');
class MyEventEmitter extends eventEmitter { }
const em = new MyEventEmitter();
em.on('event', function () {
console.log('触发事件', arguments);
})
em.emit('event', 'aa','bb','cc');
//打印结果:触发事件 [Arguments] { '0': 'aa', '1': 'bb', '2': 'cc' }
var Events = {
listens: {},
on: function (eventName, cb) {
if (this.listens[eventName]) {
this.listens[eventName].push(cb);
} else {
this.listens[eventName] = [cb];
}
},
trigger: function (eventname) {
let evt.currentTarget = this;
for (let i = 0; i < this.listens[eventname].length; i++) {
this.listens[eventname][i].call(evt.currentTarget);
}
}
}
Events.on('event', () => {
console.log('触发事件');
})
Events.trigger('event');
const fs = require('fs');
fs.unlink('/tmp/hello', (err) => {
if (err) throw err; //throw 抛出异常
console.log('已成功删除 /tmp/hello');
});
fs.writeFile(path,content,cb)
,创建一个文件,是一个异步操作,必须有回调函数。fs.writeFileSync(path,content)
,创建一个文件,是一个同步操作。let fs = require("fs");
//创建一个a.txt,并写入内容 hello!!
fs.writeFile("./a.txt","hello!!",(err)=>{
if(err) throw err;
})
fs.readFile(path,options,cb)
,读取文件的全部内容,是一个异步操作,必须有回调函数。fs.readFileSync(path)
,读取文件的全部内容,是一个同步操作。//a.txt文件
{"name":"张三"}
//js文件
let fs = require("fs");
fs.readFile("./a.txt",(err,data)=>{
if(err) throw err;
console.log(data); //
})
fs.readFile("./a.txt","utf-8",(err,data)=>{
if(err) throw err;
console.log(data); //{"name":"张三"}
})
fs.unlink(path,cb)
,删除文件,是一个异步的操作,必须有回调函数。fs.unlinkSync(path)
,删除文件,是一个同步的操作。let fs = require("fs");
// 假设 'path/file.txt' 是常规文件。
fs.unlink('path/file.txt', (err) => {
if (err) throw err;
console.log('文件已删除');
});
fs.appendFile(path,content,cb)
,在文件中追加内容,是一个异步操作,必须有回调函数。fs.appendFileSync(path,content)
,在文件中追加内容,是一个同步操作。let fs = require("fs");
// 在aa.txt中追加内容
fs.appendFile('aa.txt', 'my name is haha', (err) => {
if (err) throw err;
console.log('数据已追加到文件');
});
fs.mkdir(path,options,cb)
,创建文件夹,是一个异步操作,必须有回调函数。fs.mkdirSync(path)
,创建文件夹,是一个同步操作。let fs = require("fs");
//会报错,Error: ENOENT: no such file or directory, mkdir './tmp1/a/apple'
fs.mkdir('./tmp1/a/apple', (err) => {
if (err) throw err;
});
//在项目的根目录下创建了tmp2->a->apple,一共3个文件夹
fs.mkdir('./tmp2/a/apple', { recursive: true }, (err) => {
if (err) throw err;
});
//文件夹重复创建,会报错
//Error: EEXIST: file already exists, mkdir './tmp2/a/apple'
fs.readdir(path,cb)
,读取目录的内容,是一个异步操作,必须有回调函数。fs.readdirSync(path)
,读取目录的内容,是一个同步操作。let fs = require("fs");
//tmp文件夹下有a目录和text.txt文件
let path="./tmp"
fs.readdir(path,(err,files)=>{
console.log(files); //['a','text.txt']
})
console.log(fs.readdirSync(path)); //['a','text.txt']
fs.rmdir(path,cb)
,删除目录,是一个异步操作,必须有回调函数。fs.rmdirSync(path)
,删除目录,是一个同步操作。案例:传入文件名或者目录名,删除该文件或者该目录下所有的内容:
let fs = require("fs");
function removeFile(path){
//如果传进来的是文件,则直接删除并跳出
if(fs.statSync(path).isFile()) return fs.unlinkSync(path);
//如果是目录,递归删除该目录下的所有文件
fs.readdirSync(path).forEach((item)=>removeFile(path+"/"+item));
//最后删除这个空的目录
fs.rmdirSync(path);
}
fs.rename(oldpath,newpath,cb)
,修改目录或文件名称,是一个异步操作,必须有回调函数。fs.renameSync(oldpath,newpath)
,修改目录或文件名称,是一个同步操作。let fs = require("fs");
// 将aa.txt 重命名为bb.txt
fs.rename('aa.txt', 'bb.txt', (err) => {
if (err) throw err;
console.log('重命名完成');
});
//将目录tmp2 重命名为tmp
fs.renameSync("./tmp2","./tmp")
fs.existsSync(path)
,参数为文件夹路径。let fs = require("fs");
if (fs.existsSync('./tmp1/a')) {
console.log('文件已存在');
}
fs.stat(path,cb)
,返回文件与目录的信息,是一个异步操作,必须有回调函数。fs.statSync(path)
,返回文件与目录的信息,是一个同步操作。fs.Stats 对象
,有以下方法:
stats.isDirectory()
,如果path是文件夹,则返回 true。stats.isFile()
,如果path是文件,则返回 true。stats.size
,返回文件的大小(以字节为单位)。let fs = require("fs");
const list = ['./txtDir', './txtDir/file.txt'];
for (let i = 0; i < list.length; i++) {
//异步操作
fs.stat(list[i], function(err, stats) {
console.log(stats.isDirectory()); //true false
console.log(stats); //打印结果如下
});
}
//同步操作
console.log(fs.statSync('./txtDir').isDirectory()); //true
stats 打印结果如下:
Stats {
dev: 16777220,
mode: 33188,
nlink: 1,
uid: 501,
gid: 20,
rdev: 0,
blksize: 4096,
ino: 10803859,
size: 24,
blocks: 8,
atimeMs: 1584527766640.159,
mtimeMs: 1584527742951.745,
ctimeMs: 1584527742951.745,
birthtimeMs: 1584526535605.706,
atime: 2020-03-18T10:36:06.640Z,
mtime: 2020-03-18T10:35:42.952Z,
ctime: 2020-03-18T10:35:42.952Z,
birthtime: 2020-03-18T10:15:35.606Z }
crypto模块的目的是为了提供通用的加密和哈希算法。用纯JavaScript代码实现这些功能不是不可能,但速度会非常慢。Nodejs用C/C++实现这些算法后,通过cypto这个模块暴露为JavaScript接口,这样用起来方便,运行速度也快。
crypto 模块提供了加密功能,包括对 OpenSSL 的哈希、HMAC、加密、解密、签名、以及验证功能的一整套封装。
使用MD5、SHA1、Hmac这几种方式,对数据进行加密后,是没有办法解密的。AES的方式,可以对数据进行解密。
可以在不包括支持 crypto 模块的情况下构建 Node.js,这时调用 require(‘crypto’) 将导致抛出异常。
let crypto;
try {
crypto = require('crypto');
} catch (err) {
console.log('不支持 crypto');
}
MD5是一种常用的哈希算法,用于给任意数据一个“签名”。这个签名通常用一个十六进制的字符串表示。
调用 hash.digest() 方法之后, Hash 对象不能被再次使用
。 多次调用将会导致抛出错误。
//引入crypto模块
const crypto = require('crypto');
//使用md5模式 创建并返回一个 Hash 对象
const hash = crypto.createHash('md5');
//update 使用给定的内容更新哈希的内容
hash.update('hello world');
//digest 计算传入要被哈希(使用 hash.update() 方法)的所有数据的摘要。
let result = hash.digest('hex');
//打印出结果:5eb63bbbe01eeed093cb22bb8f5acdc3
console.log(result);
Hmac算法也是一种哈希算法,它可以利用MD5或SHA1等哈希算法。不同的是,Hmac还需要一个密钥。
只要密钥发生了变化,那么同样的输入数据也会得到不同的签名,因此,可以把Hmac理解为用随机数“增强”的哈希算法。
调用 hmac.digest() 方法之后, Hmac 对象不能被再次使用。 多次调用 hmac.digest() 将会导致抛出错误
。
//引入crypto模块
const crypto = require('crypto');
//创建 Hmac 实例
const hmac = crypto.createHmac('sha256', 'secret-key');
//使用给定的内容更新 Hmac 的内容
hmac.update('hello world');
//计算使用 hmac.update() 传入的所有数据的 HMAC 摘要
let result = hmac.digest('hex');
//打印出:095d5a21fe6d0646db223fdf3de6436bb8dfb2fab0b51677ecf6441fcf5f2a67
console.log(result);
对称加密算法,加密和解密都用相同的秘钥。
const crypto = require('crypto');
//数据加密
function aesEncrypt(data, key) {
//用指定的算法和秘钥,返回一个cipher对象
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 world';
var key = 'password';
var encrypted = aesEncrypt(data, key);
var decrypted = aesDecrypt(encrypted, key);
console.log(encrypted); //7e280d9850cd8fc1b99b2ba01f64d609
console.log(decrypted); //hello world
流(stream)是 Node.js 中处理流式数据的抽象接口。 stream 模块用于构建实现了流接口的对象。所有的流都是 EventEmitter 的实例。
Node.js 中有四种基本的流类型:
流的使用跟gulp中的pipe()类似,都是通过管道将文件进行输出。
const http = require('http');
const server = http.createServer((req, res) => {
// req 是一个 http.IncomingMessage 实例,它是可读流。
// res 是一个 http.ServerResponse 实例,它是可写流。
let body = '';
// 接收数据为 utf8 字符串,
// 如果没有设置字符编码,则会接收到 Buffer 对象。
req.setEncoding('utf8');
// 如果添加了监听器,则可读流会触发 'data' 事件。
req.on('data', (chunk) => {
body += chunk;
});
// 'end' 事件表明整个请求体已被接收。
req.on('end', () => {
try {
const data = JSON.parse(body);
// 响应信息给用户。
res.write(typeof data);
res.end();
} catch (er) {
// json 解析失败。
res.statusCode = 400;
return res.end(`错误: ${er.message}`);
}
});
});
server.listen(1337);
zlib 模块提供通过 Gzip 和 Deflate/Inflate 实现的压缩功能。
压缩或者解压数据流(例如一个文件)通过 zlib 流将源数据流传输到目标流中来完成。
const fs = require('fs');
const zlib = require('zlib');
//创建一个文件
fs.writeFileSync('log.txt', 'gp18');
//读取文件流
fs.createReadStream('log.txt')
//压缩文件
.pipe(zlib.createGzip())
//输出流
.pipe(fs.createWriteStream('log.txt.gzip'));
更多内置模块,可以查看nodejs官方文档。