event模块
- on,监听事件
- emit,发送事件
- off,移除事件。需要指定事件名和事件
- once,只执行一次
- removeAllListeners():删除全部事件,或者传入事件名来移除指定事件。
- eventNames()获取全部事件名。
- listenerCount(事件名)获取当前事件注册的个数。
- listeners(事件名)获取当前事件数组。
const EventEmitter = require("events"); const events = new EventEmitter(); // 监听事件 events.addListener("zhanghao", (...args) => { console.log("addListener监听", ...args) }) const listener1 = (...args) => { console.log("on监听", ...args) } events.addListener("llm", listener1) // events.off("llm", listener1) // 注册事件 events.removeAllListeners("llm") events.emit("zhanghao", "我的") events.emit("llm", "他的") console.log(events.listenerCount('zhanghao')) console.log(events.listeners('llm'))
Buffer
创建buffer的常用方式
- from
// 编码和解码的方式不同,解码会出现乱码。 const buf = Buffer.from("中国加油", 'utf16le'); // 对于utf-8编码的汉字而言,一个汉字等于3字节,对于utf16le编码的汉字而言,一个汉字等于2字节 console.log(buf) console.log(buf.toString("utf16le"))// 指定编码,不然默认是utf-8编码,会乱码。
- alloc(指定创建多大的缓存区)
// buf就相当于一个字节数组。没有给数组赋值时,他就相当于都是0000000...占位 const buf = Buffer.alloc(10); console.log(buf) //<Buffer 00 00 00 00 00 00 00 00 00 00> // 对buffer赋值 buf[0] = 0x90; // 为了方便展示,输出的都是16进制来表示二进制。4位等于一个16进制数。所以2个16进制数表示1字节 console.log(buf)// <Buffer 90 00 00 00 00 00 00 00 00 00>
读取二进制文件(视频,图片等非文本文件)并对其做一些事情
我们可以通过sharp库或者jimp库来对图片进行处理
// 将图片做简单的变化,然后输出 const sharp = require("sharp"); sharp('./sun.jpg') .resize(200, 200) .toFile("sunCopy.jpg")
stream流
什么是流
程序中的流也是类似的含义,我们可以想象当我们从一个文件中读取数据时,文件的二进制(字节)数据会源源不断的被读取到我们程序中。而这个一连串的字节,就是我们程序中的流。所有的流都是EventEmitter的实例
在之前学习文件的读写时,我们可以直接通过 readFile或者 writeFile方式读写文件,为什么还需要流呢?
- 直接读写文件的方式,虽然简单,但是无法控制一些细节的操作。比如从什么位置开始读、读到什么位置、一次性读取多少个字节;
- 读到某个位置后,暂停读取,某个时刻恢复读取等等;
- 或者这个文件非常大,比如一个视频文件,一次性全部读取并不合适;
Node.js中有四种基本流类型:
- Writable:可以向其写入数据的流(例如 fs.createWriteStream())。
- Readable:可以从中读取数据的流(例如 fs.createReadStream())。
- Duplex:同时为Readable和的流Writable(例如 net.Socket)。
- Transform:Duplex可以在写入和读取数据时修改或转换数据的流(例如zlib.createDeflate())。 下面我们来介绍一下可读流和可写流:
Readable可读流
以前通过fs.readFile()
来读取文件,是一次性将一个文件中所有的内容都读取到程序(内存)中。并且不能指定读取位置。所以我们就需要通过可读流
fs.createReadStream(path, options)
来读取文件。读取完毕后文件自动关闭其中options就是来对读取文件做约束的。
start
:开始读取的位置(从0开始的)
end
:结束的位置
highWaterMark
:文件一次性读取多少字节。默认64kb
encoding
: 编码格式
flags
: 文件标识。默认为r
- ...
const fs = require("fs"); const read = fs.createReadStream("./zh.txt", { // 位置是从0开始的 start: 3, end: 8, highWaterMark: 2, //文件一次性读取多少字节。默认64kb encoding: 'utf-8' }) // 可以通过pause(),resume()方法对文件读取进行暂停和恢复。 read.on("data", (data) => { console.log(data) }) read.on("open", () => { console.log("文件打开") }) read.on("close", () => { console.log("文件关闭") }) read.on("end", () => { console.log("文件读取完毕") })
Writable可写流
以前通过fs.writeFile()
来写入文件,而且只能是将源文件覆盖或者追加到源文件后面。所以我们就需要通过可读流fs.createWriteStream(path, options)
来精确地写入文件。
其中options就是来对写入文件做约束的。
start
:开始读取的位置(从0开始的)
encoding
: 编码格式
flags
: 文件标识。默认为w
。
const fs = require("fs"); const writeStream = fs.createWriteStream("./zh.txt", { flags: 'r+', // 这里可能会出现bug,应该需要指定为r+。 encoding: 'utf-8', start: 4 }) // 写入时会覆盖原来位置的文字。 writeStream.write("0000", (err) => { console.log(err) }) // end方法既可以写入东西,并且也会自动关闭文件。 writeStream.end("文件写入的内容") writeStream.on("open", () => { console.log("文件即将写入") }) // writeStream.close() writeStream.on("close", () => { console.log("文件关闭") })
pipe
连接可写流和可读流。
可读流 =====pipe=====> 可写流
const fs = require('fs'); const reader = fs.createReadStream("./zh.txt"); const writer = fs.createWriteStream("./llm.txt"); // pipe管道会自动关闭。不需要我们手动调用close事件。 reader.pipe(writer) writer.on("close", () => { console.log("文件将要关闭") })
http模块
什么是Web服务器?
当应用程序(客户端)需要某一个资源时,可以向一个台服务器,通过Http请求获取到这个资源;提供资源的这个服务器,就是一个Web服务器;
创建服务器
const http = require("http"); // 方式一 const server = new http.Server((req, res) => { res.writeHead(200, { 'content-type': 'text/html;charset:ASCII' }) res.end("<h1>我是一个汉字,当编码格式是ascii时,看其乱不乱码</h1>")//会乱码。需要设置charset:utf-8格式。 }) server.listen(8080, () => { console.log("服务器创建成功") }) // 方式二,这种方式底层其实也是调用Server类。 http.createServer((req, res) => { // 指定body中数据的形式 // req.setEncoding("utf-8") req.setEncoding("binary") req.on("data", (data) => { console.log("data", typeof data) //string res.end(data) }) // res.end("返回数据") }).listen(3030, () => { console.log("服务器启动在3030端口") })
Server通过listen方法来开启服务器,并且在某一个主机和端口上监听网络请求:
listen函数有三个参数:
- 端口port: 可以不传, 系统会默认分配端, 可以通过
server.address().port
拿到端口号,后续项目中我们会写入到环境变量中;
- 主机host: 通常可以传入localhost、ip地址127.0.0.1、或者ip地址0.0.0.0,默认是0.0.0.0;
- localhost:本质上是一个域名,通常情况下会被解析成127.0.0.1;
- 127.0.0.1:回环地址(Loop Back Address),表达的意思其实是我们主机自己发出去的包,直接被自己接收;正常的数据包经过应用层 - 传输层 - 网络层 - 数据链路层 - 物理层 ;而回环地址,是在网络层直接就被获取到了,是不会经常数据链路层和物理层的;比如我们监听 127.0.0.1时,在同一个网段下的主机中,通过主机ip地址是不能访问的;
- 0.0.0.0:监听IPV4上所有的地址,再根据端口找到不同的应用程序;比如我们监听 0.0.0.0时,在同一个网段下的主机中,通过ip地址是可以访问的;回调函数:服务器启动成功时的回调函数;
解析query参数
我们可以通过url模块解析req.url取出query。然后在通过querystring模块解析query字段生成一个对象。
const url = require("url") const queryString = require("querystring") const {query} = url.parse(req.url); const queryObj = queryString.parse(query);
url解析
const url = require("url"); console.log(url.parse("http://127.0.0.1/user/1?name=zh&age=20#llm"))
解析请求体中的参数
由于request是流。所以我们读取post请求传入的数据,我们需要通过监听data事件来读取,然后再将二进制流转为字符串。然后在通过JSON.parse()
解析字符串。
let dataBody = "" // 设置body的编码 req.setEncoding("utf-8") req.on("data", (data) => { dataBody += data; // 这里拼接是为了当一次输入类型不能完全获取,需要多次拼接,才能获取完整数据。 }) req.on("close", () => { // 当文件关闭之前,解析参数 const requestData = JSON.parse(dataBody) console.log(requestData) })
请求和响应的一些字段
请求头字段
content-type: 指定传入的数据的类型。请求体类型
- application/json表示是一个json类型;
- text/plain表示是文本类型;
- application/xml表示是xml类型;
- multipart/form-data表示是上传文件;
content-length: 传递的数据长度。
accept-encoing:告诉服务器,客户端支持的文件压缩格式。
accept: 告诉服务器,客户端可以接受文件的格式类型。
响应response
只能通过res.end()方法来结束响应。并且返回响应。
Write方法:这种方式是直接写出数据,但是并没有关闭流。 设置状态码:
- res.statusCode = “”。
- res.writeHead("状态码", {响应头})
设置响应头
- res.setHeader()
注意:text/html表示响应字段返回的是一个html格式的,可以直接渲染在页面中,但是application/html,表示下载html文件。
http中发送网络请求
回调函数的参数类型和http开启一个服务中的request参数类型是一样的,都是incomingMessage。所以需要通过监听data事件来获取结果。
- 发送get请求
const http = require('http'); // http发送get请求 http.get('http://localhost:8888', (res) => { res.on('data', (data) => { console.log(data.toString()); }); res.on('end', () => { console.log("获取到了所有的结果"); }) })
- 发送post请求。注意这里需要调用end方法,告诉服务器请求结束,否则会发生堵塞。
const req = http.request({ method: 'POST', hostname: 'localhost', port: 8888 }, (res) => { res.on('data', (data) => { console.log(data.toString()); }); res.on('end', () => { console.log("获取到了所有的结果"); }) }); req.end();
但是一般使用axios库来发送网络请求。