1. fs 文件系统模块
fs
文件系统操作模块- API参考地址
- 文件操作的三种方式
- 方式一:
同步
操作文件:代码会被阻塞
,不会继续执行; - 方式二:
异步回调
函数操作文件:代码不会被阻塞,需要传入回调函数,当获取到结果时,回调函数被执行; - 方式三:
异步Promise操作文件
:代码不会被阻塞,通过 fs.promises 调用方法操作,会返回一个Promise,可以通过then、catch进行处理;
- 方式一:
1.1 文件读取模块
// fs 模块读取文件
const fs = require('fs');
// 第一个参数是文件路径,第二个参数传递对象,encoding的意思是读取文件以什么的方式进行解码
// 同步
const fileFs = fs.readFileSync('./01text.txt',{
encoding:"utf-8"
})
console.log(fileFs);
// 异步读取分普通异步和promise读取=>异步读取会发送回调地狱,因此一般使用promise的方式
// 第三个参数为函数,接收的值为error 与 data
fs.readFile('./01text.txt',{encoding:"utf-8"},(error,data)=>{
console.log(data);
})
// 3. 异步读取promise
fs.promises.readFile('./01text.txt',{
encoding:'utf-8'
}).then(arr=>{
console.log(arr)
}).catch(err=>{})
1.2 文件描述符 fd
- 每个打开的文件都分配了一个称为
文件描述符的简单的数字标识符
。 所有文件系统操作都使用这些文件描述符来标识和跟踪每个特定的文件
fstat(文件描述符,回调)
读取文件信息
const fs = require('fs');
fs.open('./02text.txt', (err, fd) => {
// fd就是文件描述符 , 为number类型
// fs.writeFile(fd,()=>{})
// 2.fstat 读取文件信息
fs.fstat(fd, (err, stats) => {
console.log(stats);
// 3. 关闭文件服务
fs.close(fd)
})
})
1.3 文件写入
writeFile(路径,数据,编码与写入方式,回调)
- flag读写方式配置
w
打开文件写入,默认值;w+
打开文件进行读写(可读可写),如果不存在则创建文件;r
打开文件读取,读取时的默认值;r+
打开文件进行读写,如果不存在那么抛出异常;a
打开要写入的文件,将流放在文件末尾。如果不存在则创建文件;a+
打开文件以进行读写(可读可写),将流放在文件末尾。如果不存在则创建文件
const fs = require('fs');
const msg='文件写入 写入到text.txt 中'
// 第一个参数为文件路径,如果文件路径不存在,则自动创建
// 第二个参数为要写入的数据 ,
// 第四个为失败的回调
fs.writeFile('./text.txt',msg,(err)=>{
if (err) {
console.log('写入文件错误')
}
})
// 第三个参数 为对象,里面传入编码格式encoding 以及文件如何读取 flag
fs.writeFile('./text.txt', msg,{
encoding:'utf-8',
flag:'a' // flag 的值主要代表写入时,是追加还是覆盖 a追加 w 默认值写入覆盖
}, (err) => {
if (err) {
console.log('写入文件错误')
}
})
1.4 创建文件夹操作 mkdir
mkdir
创建文件夹 , 第一个参数路径
第二个为失败的回调
const fs = require('fs');
// mkdir 创建文件夹 , 第一个参数路径 第二个为失败的回调
fs.mkdir('./why', err => {
console.log(err);
})
1.5 读取文件目录readdir
- 读取目录的内容, 回调有两个参数 (err, files),其中 files 是当前目录中文件的名称的数组。可选的 options 参数可以是字符串(指定字符编码)、或具有 encoding 属性(指定用于传给回调的文件名的字符编码)的对象。
- 如果 encoding 被设置为 ‘buffer’,则返回的文件名会作为 Buffer 对象传入。
- 如果 options.
withFileTypes 被设置为 true,则 files 数组会包含Symbol(type) 类型 <fs.Dirent>对象
- 读取的文件夹下面是文件还是文件夹看返回值是1 还是 2 ,
2 文件夹
,1文件
isDirectory()
判断是否是文件夹
const fs = require('fs');
// readdir 读取文件夹files返回数组
fs.readdir('./why', (err, files) => {
console.log(files);
})
// 第二个参数是读取文件夹
fs.readdir('./why/', { withFileTypes: true }, (err, files) => {
console.log(files); // 读取的文件夹下面是文件还是文件夹看返回值是1 还是 2 ,2 文件夹,1文件
files.forEach(item => {
// isDirectory() 函数判断是文件夹还是文件
if (item.isDirectory()) {
console.log('文件夹', item.name);
} else {
console.log('文件', item.name);
}
})
})
// 递归读取多个文件夹下面的文件
function readDirectory(path) {
fs.readdir(path, { withFileTypes: true }, (err, files) => {
files.forEach(item => {
if (item.isDirectory()) {
readDirectory(`${path}/${item.name}`)
}else{
console.log(item.name);
}
})
})
}
readDirectory('./why')
1.6 获取文件信息 stat
- 作用:获取文件信息,判断文件状态(是文件还是文件夹)
- 回调中的stats
stats.isFile()
判断是否是文件stats.isDirectory()
判断是否是文件夹
const fs = require('fs');
fs.stat('./a.txt',(err,stats) => {
if (err) throw err;
console.log(stats);
})
1.7 rename 文件夹或者文件重命名
- 格式 :
rename(旧路径,新路径,回调)
const fs = require('fs');
// rename 文件夹或者文件重命名
fs.rename('./why','newWhy',(err)=>{
})
文件操作参考 :参考一 : yma16
参考二 :xt_123456
2. events模块
-
Events 模块它提供了一个属性
EventEmitter
,EventEmitter 的核心是事件发射与事件监听器
。 -
Node中大部分的模块,都继承自 Events 模块。
- Events 模块是Node对 发布订阅模式(publish/subscribe)的实现。一个对象通过这个模块,向另一个对象传递消息。
- 该模块通过 EventEmitter 属性,提供了一个构造函数。该构造函数的实例具有
on
方法,可以用来监听指定事件,并触发回调函数。 - 任意对象都可以发布指定事件,被 EventEmitter 实例的on方法监听到。
-
Events 发布订阅API
- 订阅方法:on 方法用来订阅事件,订阅是将方法对应成一种一对多的关系。
- 发布方法:emit 用来执行订阅的事件。
- 取消订阅:off 方法可以移除对应的事件监听。
- 订阅一次:once 绑定事件当执行后自动删除订阅的事件
const events = require('events');
// 1.创建events实例
const emmiter = new events()
function handlyOnFn(...args) {
console.log('监听事件',args)
}
// 监听事件
emmiter.on('why', handlyOnFn)
setTimeout(() => {
// 2. 发送事件传递参数
emmiter.emit('why','1111111111111')
// 3. 取消事件
emmiter.off('why', handlyOnFn)
setTimeout(() => {
emmiter.emit('why')
}, 1000);
}, 2000);
2.1 events的实例属性与方法
emitter.eventNames()
:返回当前 EventEmitter对象注册的事件字符串数组;emitter.getMaxListeners()
:返回当前 EventEmitter对象的最大监听器数量,可以通过setMaxListeners()来修改,默认是10;emitter.listenerCount(事件名称)
:返回当前 EventEmitter对象某一个事件名称,监听器的个数;emitter.listeners(事件名称)
:返回当前 EventEmitter对象某个事件监听器上所有的监听器数组;emitter.once(eventName, listener)
:事件监听一次emitter.prependListener()
:将监听事件添加到最前面emitter.prependOnceListener()
:将监听事件添加到最前面,但是只监听一次emitter.removeAllListeners([eventName])
:移除所有的监听器
3. Buffer和二进制
3.1 buffer与字符串的编码
// buffer 主要操作二进制文件
const buf=new Buffer('HELLO')
console.log(buf);
const buf02=Buffer.from('HELLO')
console.log(buf02); // <Buffer 48 45 4c 4c 4f>
// 中文 用三个字节表示
const zhbuf=Buffer.from('你好啊')
console.log(zhbuf); // <Buffer e4 bd a0 e5 a5 bd e5 95 8a>
// 指定编码格式
const zhbuf02=Buffer.from('你好啊','utf16le')
console.log(zhbuf02); // <Buffer 60 4f 7d 59 4a 55>
// 注意点 编码格式要是传了,解码格式要和编码格式保持一致
console.log(zhbuf02.toString('utf16le'));
3.2 buffer其他创建方式
const fs = require('fs');
// const buf=Buffer.from()
// 三个属性 ,第一个属性为size表示申请多大的字节大小空间
const buf = Buffer.alloc(8)
console.log(buf); // <Buffer 00 00 00 00 00 00 00 00>
// 2.手动对字节进行访问
console.log(buf[0]); // 0
// 3. 手动对字节操作
buf[0] = 100
console.log(buf); // <Buffer 64 00 00 00 00 00 00 00>
console.log(buf[0].toString()); // 100
3.2 文件读取字节编码
const fs = require('fs');
// 不指定编码格式
fs.readFile('./a.txt', (err, date) => {
console.log(date); // <Buffer 68 65 6c 6c 6f 20 77 6f 72 64>
})
// 文件读取修改
fs.readFile('./a.txt', (err, date) => {
date[0]=12
console.log(date); // <Buffer 0c 65 6c 6c 6f 20 77 6f 72 64>
})
// 读取图片文件 (sharp 读取文件进行处理,并写入文件)
fs.readFile('../map/1.进制转换.png',(err,data)=>{
console.log(data);
})
4. stream文件流读取
stream
是为了解决在文件流读取中,重什么时候开始,一次读取多少字节,文件暂停后继续读取的API模块- Node.js中有四种基本流类型:
- Writable:可以向其写入数据的流(例如 fs.createWriteStream())。
- Readable:可以从中读取数据的流(例如 fs.createReadStream())。
- Duplex:同时为Readable和Writable(例如 net.Socket)。
- Transform:Duplex可以在写入和读取数据时修改或转换数据的流(例如zlib.createDeflate())
4.1 Readable读取
- 一开始使用的是readFile读取,但是
文件过大、读取的位置、结束的位置、一次读取的大小;
这个API都是不可控的,这个时候,就要使用createReadStream
,
const fs = require('fs');
fs.readFile('./a.txt',(err,data)=>{
console.log(data);
})
- start 开始位置
- end 结束
- highWaterMark 一次读取多少
const fs = require('fs');
// fs.readFile('./a.txt',(err,data)=>{
// console.log(data);
// })
// 上面的代码在文件读取时,是一次性读取的,
// 缺点1. 没办法精准控制从哪里读取,读取到什么程度
// 2. 读取到某个位置时,暂停读取,恢复读取
// 3. 文件非常大的时候,不能多次读取
const readStream = fs.createReadStream('./a.txt', {
// start end 开始结束 end取闭区间
start: 2,
end: 15,
highWaterMark: 3 // 每次读取多少
})
readStream.on('data', data => {
console.log(data.toString()); // llo W
readStream.pause() // 暂停读取
setTimeout(() => {
readStream.resume() // 读取
}, 2000);
})
- 在读取或读取完成时,也可以做其他操作,比如关闭,恢复,暂停等
- 每一个文件在读取完成时,应该都要关闭,这样就不会在占用一个进程了
const fs = require('fs');
const readStream = fs.createReadStream('./a.txt', {
start: 2,
end: 15,
highWaterMark: 3 // 默认读取是64kb
})
// 监听读取的事件
readStream.on('data', data => {
console.log(data.toString());
readStream.pause()
setTimeout(() => {
readStream.resume()
}, 2000);
})
// open 文件打开的监听
// fd文件描述符
readStream.on('open', fd => {
console.log('文件打开的监听事件', fd);
})
// end 数据读取完成
readStream.on('end',()=>{
console.log('文件读取完成',)
})
// close 关闭文件
readStream.on('close',()=>{
console.log('关闭回调')
})
4.2 Writable 可写流
- 下面的第一个参考代码用的是
writeFile
模块进行写入,但是这样也会存在一个问题,就是文件不能精准写入某个位置
- 这个时候,就可以使用
createWriteStream
const fs = require('fs');
fs.writeFile('./b.txt', 'Hello Node', {
encoding: 'utf-8',
flag: 'a+'
}, (err, data) => {
console.log('写入文件', data)
})
- createWriteStream 函数中的第二个参数
flags
:默认是w,如果要追加写入,可以使用 a或者 a+;start
:写入的位置;注意点
在对中间数据插入信息时,存在兼容性问题,写入兼容性问题windows 用 r+ ,Mac用a+
const fs = require('fs');
// fs.writeFile('./b.txt', 'Hello Node', {
// encoding: 'utf-8',
// flag: 'a+'
// }, (err, data) => {
// console.log('写入文件', data)
// })
// 1. 创建写入流
const writeStream = fs.createWriteStream('./b.txt', {
//
flags: 'a+'
})
// 2.文件打开
writeStream.on('open',fd=>{
console.log(fd);
})
writeStream.write(' 文件写入流')
writeStream.write(' 再次写入文件', err => {
// console.log(err);
})
//3. 写入文件,手动关闭文件
// writeStream.close()
// 4. end方法将最后的内容写入,并关闭文件
writeStream.end('最后写入文件并关闭')
// 5. 写入结束
writeStream.on('finish',()=>{
console.log('写入完成')
})
4.3 Writable 可写流的关闭close
- 写入流并不能监听close事件
- 这是因为写入流在打开后是不会自动关闭的,因此必须手动关闭,来告诉Node已经写入结束了; 并且会发出一个 finish 事件的;
writeStream.on('finish',()=>{
console.log('写入完成')
})
end
写入并关闭文件
// 4. end方法将最后的内容写入,并关闭文件
writeStream.end('最后写入文件并关闭')
4.4 文件拷贝
- 在文件的拷贝中,可以利用建立管道的方式
pipe方法 :
- 可以将读取到的 输入流,手动的放到 输出流中进行写入
const fs = require('fs');
// 文件写入 ,注意data
fs.readFile('./c.txt', (err, data) => {
fs.writeFile('./d.txt', data, (err, w) => {
console.log('w', w)
})
})
// 第二种方式文件写入
const readstream = fs.ReadStream('./c.txt')
const writestream = fs.WriteStream('./e.txt')
readstream.on('data', data => {
writestream.write(data)
})
readstream.on('end', () => {
writestream.close()
})
// 第三种方式 , 建立管道写入
const readstream01 = fs.ReadStream('./c.txt')
// fs.createReadStream() == fs.ReadStream()
const writestream01 = fs.WriteStream('./F.txt')
readstream01.pipe(writestream01)
5. http模块
- 知识点补充 URLSearchParams
解析参数
URLSearchParams
构造器创建并返回一个新的URLSearchParams 对象。 开头的'?'
字符会被忽略。- 提供的方法:
has() 查询指定键名是否存在 返回一个Boolean值
get() 获取指定搜索参数的第一个值。
getAll() 获取指定搜索参数的所有值,返回是一个数组。
toString() 返回搜索参数组成的字符串,可直接使用在URL上。
append() 插入一个指定的键/值对作为新的搜索参数
set() 设置一个搜索参数的新值,如果原来有多个值将删除其他所有的值。
delete() 从搜索参数列表里删除指定的搜索参数及其对应的值。
entries() 返回一个iterator,允许遍历该对象中包含的所有键/值对。每一组键值对都是 USVString对象
let paramsString = "?key=java&q=你好啊&q=我叫张三"
let query = new URLSearchParams(paramsString);
// has() 查询指定键名是否存在
console.log(query.has("q") );// true
// get() 获取指定搜索参数的第一个值。
console.log(query.get("q")); // "你好啊";
// getAll() 获取指定搜索参数的所有值,返回是一个数组。
console.log(query.getAll("q"));; // ['你好啊', '我叫张三']
// toString() 返回搜索参数组成的字符串,可直接使用在URL上。
// 我这里使用decodeURI对获取的字符串进行了一下解码 不解码就会乱码
console.log(decodeURI(query.toString())); // 'key=java&q=你好啊&q=我叫张三'
// append() 插入一个指定的键/值对作为新的搜索参数
query.append("q", "我是通过append添加的"); // 添加>>>> q=我是通过append添加的
console.log(decodeURI(query.toString())); // 'key=java&q=你好啊&q=我叫张三&q=我是通过append添加的'
// set() 设置一个搜索参数的新值,如果原来有多个值将删除其他所有的值
query.set("q", "halo!!!")
console.log(decodeURI(query.toString())); // 'key=java&q=halo!!!'
// delete() 从搜索参数列表里删除指定的搜索参数及其对应的值。
query.delete("q");
console.log(decodeURI(query.toString())); // 'key=java&q=halo!!!'
5.1 http 创建服务器
- 通过
createServer
来创建服务器,http.createServer会返回服务器的对象; - 创建Server时会传入一个回调函数,这个回调函数在被调用时会传入两个参数:
req
:request请求对象,包含请求相关的信息;res
:response响应对象,包含我们要发送给客户端的信息
安装 npm i nodemon -g // 代码更新服务器自动重启
const http = require('http');
// 1. 创建服务器
const server = http.createServer((request, response) => {
// 客户端发送请求时携带的数据,method,url 都在request 查看
// 返回结果都在response中
response.end('hello node')
})
// 2. 开启服务器
server.listen(8000, () => {
console.log('服务器开启~')
})
// createServer 也可以创建多个服务器
注意点
浏览器访问请求的次数
const http = require('http');
// 这里利用游览器访问执行了两次
const server = http.createServer((req, res) => {
console.log('访问')
})
server.listen(8000, () => {
console.log('开启服务器💚')
})
// nodemon -g 使用代码更新服务器自动重启
// 使用 nodemon
5.2 request对象
- 在向服务器发送请求时,前端会携带很多信息例如:
- 本次请求的
URL
,服务器需要根据不同的URL进行不同的处理; - 本次请求的
请求方式
,比如GET、POST请求传入的参数和处理的方式是不同的 - 本次请求的
headers
中也会携带一些信息,比如客户端信息、接受数据的格式、支持的编码格式等;
- 本次请求的
const http = require('http');
const server = http.createServer((req, res) => {
// 1. url
console.log(req.url);
// 方式 method
console.log(req.method);
// 3. 请求头信息
console.log(req.headers);
})
server.listen(8000, () => {
console.log('开启服务器💚')
})
5.3 url与method的区分
- 服务器端根据不同的请求地址,与请求方式,作出不同的响应:
const http = require('http');
const server = http.createServer((req, res) => {
if (req.url = '/login') {
console.log('登录成功')
res.end('登录成功~')
} else {
console.log('其他操作')
}
})
server.listen(8000, () => {
console.log('开启服务器💚')
})
const http = require('http');
const server = http.createServer((req, res) => {
if (req.url === '/login' && req.method === 'POST') {
res.end('登录成功')
} else {
res.end('检测请求方式或API路径')
}
})
server.listen(8000, () => {
console.log('开启服务器💚')
})
5.4 query与body参数数据
- 用户发送请求,
http://localhost:8000/login?name=admin&password=12356;
- 获取
/login?name=admin&password=12356;
对其进行解析
const http = require('http');
const url = require('url'); // 解析url
const server = http.createServer((req, res) => {
console.log(req.url); // /home?offset=100&limt=1
const queryString = url.parse(req.url)
// 1.解析url
// res.end(queryString.query) // offset=100&limt=1
// 2. 解析query
const searchUrl = new URLSearchParams(queryString.query)
console.log(searchUrl.toString());
})
server.listen(8000, () => {
console.log('开启服务器💚')
})
- body 参数解析
- 获取body携带的数据,需要通过监听req的 data事件来获取
- 通过JSON.parse方法转换为对象类型
注意编码格式
利用setEncoding
设置编码为utf-8
const http = require('http');
const server = http.createServer((req, res) => {
// 1. 编码
req.setEncoding('utf-8')
// 2. 数据 buffer
req.on('data', data => {
console.log(data);
}),
// 3. 登录
req.on('end', () => {
res.end('登录成功')
})
})
server.listen(8000, () => {
console.log('开启服务器💚')
})
5.5 headers
- 在request对象的header中包含客户端传递过来一些信息:
application/x-www-form-urlencoded
:表示数据被编码成以 ‘&’ 分隔的键 - 值对,同时以 ‘=’ 分隔键和值application/json
:表示是一个json类型;text/plain
:表示是文本类型;application/xml
:表示是xml类型;multipart/form-data
:表示是上传文件;
const http = require('http');
const server = http.createServer((req, res) => {
const { headers } = req
console.log(headers); // content-type 文件类型
// content-length 数据总长度
// accept-encoding 接收的验收格式
})
server.listen(8000, () => {
console.log('开启服务器💚')
})
5.6 response 响应
- 如果给客户端响应的结果数据,可以通过两种方式:
Write方法
:这种方式是直接写出数据,但是并没有关闭流;end方法
:这种方式是写出最后的数据,并且写出后会关闭流;
const http = require('http');
const server = http.createServer((req, res) => {
res.write('写出数据---')
res.end('写出数据结束') // 这里利用end结束关闭,不能调用close
})
server.listen(8000, () => {
console.log('开启服务器💚')
})
注意点
:- 不能调用close方法
- 如果不关闭响应结果会一直等待响应
5.6.1 状态码
- 可以通过
statusCode
设置状态码
const server = http.createServer((req, res) => {
// 响应状态码
res.statusCode = 201
res.end('写出数据结束')
})
5.6.2编码类型
- 返回头部信息,主要有两种方式:
res.setHeader
:一次写入一个头部信息;res.writeHead
:同时写入header和status;
Content-Type
设置客户端接收信息方式 ,客户端默认接收的字符串
const http = require('http');
const server = http.createServer((req, res) => {
// 返回编码格式-游览器解析
res.setHeader('Content-Type','application/json;charset=utf8')
res.end('返回编码格式-游览器解析')
// 2. 和状态码一起设置
res.writeHead(200,{
'Content-Type':'application/json;charset=utf8'
})
})
server.listen(8000, () => {
console.log('开启服务器💚')
})
5.7 http请求
- axios库可以在浏览器中使用,也可以在Node中使用:
- 在浏览器中,axios使用的是封装xhr;
- 在Node中,使用的是http内置模块;
// axios
import axios from 'axios';
axios.get('http:localhost:8000').then(() => {
console.log(res);
})
// 2. node
const http = require('http');
// http.get('http:localhost:8000',res=>{
// res.on('data',data=>{
// console.log(data);
// })
// })
// post 请求
const req = http.request({
method: 'POST',
hostname: 'localhost',
port: 8000
}, err => {
err.on('data', data => {
console.log(data);
})
})
req.end()