1. fs模块
1.1 读写和追加文件内容
const fs = require('fs')
// 读取文件内容
fs.readFile('./a.txt', 'utf8', (err, data) => {
console.log(err);
console.log(data);
})
// 写入文件内容
fs.writeFile('./a.txt', 'aaaaaaa', (e) => {
console.log(e);
})
// 读取文件夹
const files = fs.readdirSync('books')
// 追加文件内容
// 第一种
fs.appendFile('./a.txt', '我是要追加的内容', err => {
console.log(err)
})
// 第二种(a是追加append, w是写入write, r是读read, 默认是w)
fs.writeFile('./a.txt', '我要追加在尾部', {flag: 'a'}, (err) => {
console.log(err);
})
// 第三种
fs.readFile('./a.txt', 'utf8', (e, data) => {
if (!e) {
const newData = `${data}???`;
fs.writeFile('./a.txt', newData, e => {
if (!e) {
console.log('追加内容成功啦');
}
})
}
})
1.2 文化流式读取与写入
// 文件流式读取
const fs = require('fs')
// 创建读取流对象
const rs = fs.createReadStream('./file.mp4')
// 绑定data事件
rs.on('data', chunk => {
console.log(chunk); // chunk.length 65536字节 => 64kb
})
rs.on('end', () => {
console.log('读取完成');
})
// 文件流式写入
const fs = require('fs')
const ws = fs.createWriteStream('./text.txt')
ws.write('第1段内容')
ws.write('第2段内容')
ws.write('第3段内容')
ws.write('第4段内容')
wx.close(); // 关闭通道
1.3 练习: 文件复制
// 复制文件
const fs = require('fs')
const process = require('process')
// 第一种方式, 占用内存空间大
// const res = fs.readFileSync('./aaa.jpg')
// fs.writeFileSync('./bbb.jpg', res)
// console.log(process.memoryUsage()); // rss 32546816字节
// 第二种方式, 占用内存空间小
// const rs = fs.createReadStream('./aaa.jpg')
// const ws = fs.createWriteStream('./ccc.jpg')
// rs.on('data', (chunk) => {
// ws.write(chunk)
// })
// rs.on('end', () => {
// console.log(process.memoryUsage()); // rss
// })
// 第三种方式, 占用内存空间最小
const rs = fs.createReadStream('./aaa.jpg')
const ws = fs.createWriteStream('./ccc.jpg')
rs.pipe(ws)
console.log(process.memoryUsage()); // rss
1.4 文件重命名和文件移动操作
const fs = require('fs')
// 第一个参数是要操作文件的路径, 第二个参数是修改完文件路径后的名称和未知, 第三个是修改完毕后的回调函数
// 文件重命名
// fs.rename('./aaa.jpg', './aaa1.jpg', err => {
// if (err) {
// return console.log(err);
// }
// console.log('成功');
// })
// 文件移动(cs文件夹要创建否则会报错)
fs.rename('./aaa1.jpg', './cs/aaa.jpg', err => {
if (err) {
return console.log(err);
}
console.log('成功');
})
1.5 文件删除
const fs = require('fs')
// 文件删除(unlink和rm都能删除, 其中rm是node14.4版本后新加的属性)
// fs.unlink('./cs/ccc.jpg', err => {
// if (err) {
// return console.log('删除失败')
// }
// console.log('删除成功');
// })
fs.rm('./ccc copy.jpg', err => {
if (err) {
return console.log('删除失败')
}
console.log('删除成功');
})
1.6 文件夹操作(创建,读取和删除)
const fs = require('fs')
// 文件夹操作
// 第一个参数是文件夹路径, 第二个参数选项配置, 第三个参数是操作后的回调
// 单个创建文件夹
// fs.mkdir('./html', err => {
// if (err) {
// return console.log('创建失败');
// }
// console.log('创建成功');
// })
// 递归创建文件夹
// fs.mkdir('./a/b/c', {recursive: true}, err => {
// if (err) {
// return console.log('创建失败');
// }
// console.log('创建成功');
// })
// 读取文件夹
fs.readdir('./html', (err, data) => {
if (err) {
return console.log('失败');
}
console.log(data, '成功');
})
// 递归删除文件夹, 即文件夹非空(fs.rmdir也可以删除, 但不推荐, 以后的版本可能删除)
fs.rm('./a', {recursive: true}, err => {
if (err) {
return console.log('失败')
}
console.log('成功');
})
1.7 查看资源状态以及判断该资源是文件夹还是文件
const fs = require('fs')
// 查看资源状态
fs.stat('./aaa.jpg', (err, data) => {
if (err) return console.log(err);
// 该资源是否是文件
console.log(data.isFile());
// 该资源是否是文件夹
console.log(data.isDirectory());
})
1.8 练习, 批量重命名文件名字, 往前面数字加0
const fs = require('fs')
// 练习, 批量重命名文件名字, 往前面数字加0
const files = fs.readdirSync(__dirname, '/code');
files.forEach(item => {
// 拆分文件名
let [num, name] = item.split('-')
if (Number(num) < 10) {
num = `0${num}`
}
const newName = `${num}${name}`
fs.renameSync(`./code/${item}`, `./code/${newName}`)
})
2. path模块
2.1 获取文件后缀名
// 获取文件后缀名
const path = require('path')
const filePath = __dirname + '/login.ico'
const ext = path.extname(filePath)
console.log(ext) // .ico
3. http模块
3.1 请求体练习
// 路径是/login, 响应体返回登录页面, 路径是/register, 返回注册页面, 其余返回Not Found
const http = require('http')
const server = http.createServer((request, response) => {
// 设置响应头为utf-8否则中文会乱码
response.setHeader('content-type', 'text/html;charset=utf-8')
const {method} = request;
// URL可以只传一个参数(如http://www.baidu.com/search?a=1&b=2)
// 也可以传两个参数, 如下方案例, 如果使用/search?a=1&b=2这种路径, 那么第二个参数必须传域名否则会报错
const {pathname} = new URL(request.url, 'http://127.0.0.1');
if (method === 'GET' && pathname === '/login') {
response.end('登录页面')
} else if (method === 'GET' && pathname === '/register') {
response.end('注册页面')
} else {
response.end('Not Found')
}
})
server.listen(9000, () => {
console.log('启动成功');
})
3.2 实现网页引入外部资源+mime+解决乱码问题+完善错误处理
// 实现网页引入外部资源
// const http = require('http')
// const fs = require('fs')
// const server = http.createServer((request, response) => {
// const {pathname} = new URL(request.url, '127.0.0.1')
// if (pathname === '/') {
// const html = fs.readFileSync(__dirname + '/index.html')
// response.end(html)
// } else if (pathname === '/index.css') {
// const css = fs.readFileSync(__dirname + '/index.css')
// response.end(css)
// } else if (pathname === '/index.js') {
// const js = fs.readFileSync(__dirname + '/index.js')
// response.end(js)
// } else {
// response.statusCode = 404
// response.end('<h1>404 Not Found</h1>')
// }
// })
// server.listen(9000, () => {
// console.log('启动成功');
// })
// 优化: 实现网页引入外部资源
// 设置mime类型
// 对于未知的资源类型, 可以选择application/octet-stream类型,浏览器在遇到该类型的响应时,会对响应体内容进行独立存储,也就是我们常见的下载效果
const mimes = {
html: 'text/html',
css: 'text/css',
js: 'text/js',
png: 'text/png',
jpeg: 'text/jpeg',
gif: 'text/gif',
mp3: 'text/mp3',
mp4: 'text/mp4',
json: 'application/json',
}
// 优化: 实现网页引入外部资源
const http = require('http')
const fs = require('fs')
const path = require('path')
const server = http.createServer((request, response) => {
// 只能发送GET请求
if (request.method !== 'GET') {
response.statusCode = 405;
response.end('<h1>405 Method Not Allowed</h1>')
}
const {pathname} = new URL(request.url, '127.0.0.1')
const filePath = __dirname + pathname;
fs.readFile(filePath, (err, data) => {
if (err) {
switch (err.code) {
case 'ENOENT':
response.statusCode = 404
response.end('<h1>404 Not Found</h1>')
break;
case 'EPERM':
response.statusCode = 403
response.end('<h1>403 Forbidden</h1>')
break;
default:
response.statusCode = 500
response.end('<h1>Internal Server Error</h1>')
break;
}
return;
}
const ext = path.extname(filePath).slice(1) // path.extname(filePath)的结果是.css这种包含点的所以要去掉
const type = mimes[ext];
if (type) {
// response.setHeader('content-type', type) // 比如结果是text/html, 它没加;charset=utf-8所以会乱码
// 如果是html文件, 添加charset=utf-8
if (ext === 'html') {
response.setHeader('content-type', `${type};charset=utf-8`) // 解决乱码问题
} else {
response.setHeader('content-type', type)
}
} else {
response.setHeader('content-type', 'application/octet-stream')
}
// 响应文件内容
response.end(data)
})
})
server.listen(9000, () => {
console.log('启动成功');
})
4. express
4.1 获取请求报文参数
const express = require('express')
const app = express();
app.get('/', (req, res) => {
// 原生操作
console.log(req.method)
console.log(req.url)
console.log(req.httpVersion)
console.log(req.headers)
// express 的操作
console.log(req.path)
console.log(req.query)
// 获取ip
console.log(req.ip)
// 获取请求头
console.log(req.get('host'))
})
app.listen(9000, () => {
console.log('启动成功')
})
4.2 练习: 路由参数获取
// 浏览器输入 http:127.0.0.1:9000/1.html 页面返回 {"id":1}
// 浏览器输入 http:127.0.0.1:9000/2.html 页面返回 {"id":2}
// 浏览器输入 http:127.0.0.1:9000/3.html 页面返回 404 not found
const express = require('express')
const {list} = require('./data.json')
const app = express();
app.get('/:id.html', (req, res) => {
// 浏览器输入 http:127.0.0.1:9000/1.html 后获取路由参数
const {id} = req.params;
console.log(list);
const result = list.find(item => item.id == id);
if (result) {
res.end(JSON.stringify(result))
} else {
res.end('404 not found')
}
})
app.listen(9000, () => {
console.log('启动成功')
})
4.3 一般响应设置和其他响应设置
const express = require('express')
const app = express()
app.get('/login', (req, res) => {
// 原生响应
// res.statusCode = 404
// res.statusMessage = 'love'
// res.setHeader('xxx', 'bbb')
// res.write('write')
// res.end('end')
// express响应
// res.status(500)
// res.set('aaa', 'bbb')
// res.send('你好send')
// res.status(500).set('abd', 'def').send('这都是OK的')
// 跳转响应
// res.redirect('http://www.baidu.com')
// 下载响应
// res.download(__dirname + '/data.json')
// JSON响应
// res.json({name: 'name'})
// 响应文件内容
res.sendFile(__dirname + '/index.html')
})
app.listen(9000, () => {
console.log('启动成功');
})
4.4 全局中间件和路由中间件练习
// 全局中间件: 记录每个请求的 url 和 ip 地址
const express = require('express')
const fs = require('fs')
const path = require('path')
const {promisify} = require('util')
const appendFile = promisify(fs.appendFile)
const app = express()
// 声明中间件函数
async function recordMiddleware(req, res, next) {
const {url, ip} = req;
await appendFile(path.resolve(__dirname, './access.log'), `${url} ${ip} \r\n`)
next();
}
// 使用中间件函数
app.use(recordMiddleware)
app.get('/login', (req, res) => {
res.send('login')
})
app.get('/user', (req, res) => {
res.send('user')
})
app.get('*', (req, res) => {
res.send('404')
})
app.listen(9000, () => {
console.log('启动成功');
})
// 路由中间件
// 针对/login /user的请求, 要求url携带code=521参数, 如未携带提示: 暗号错误
const express = require('express')
const app = express()
const checkCodeMiddleware = (req, res, next) => {
// 如果url参数code等于521
if (req.query.code === '521') {
next();
} else {
res.send('暗号错误')
}
}
app.get('/login', checkCodeMiddleware, (req, res) => {
res.send('login')
})
app.get('/user', checkCodeMiddleware, (req, res) => {
res.send('user')
})
app.get('*', (req, res) => {
res.send('404')
})
app.listen(9000, () => {
console.log('启动成功');
})
4.5 静态资源中间件 express.static
const express = require('express')
const app = express()
// 静态资源中间件设置
/**
* 设置之后, public下如果有index.html文件的话, 使用localhost:9000 或者 localhost:9000/index.html就能获取文件信息了
* 如果有app.css文件, 使用 localhost:9000/app.css 也能获取css的内容
*
* 注意事项
* 1. 如果public有index.html文件, 单独也有index.html路由(app.get('/index.html')), 则谁书写在前面谁优先级就最高
* 2. index.html文件为默认打开的资源
* 3. 路由响应动态资源, 静态资源中间件响应静态资源
*/
app.use(express.static(__dirname + '/public'))
const checkCodeMiddleware = (req, res, next) => {
if (req.query.code === '521') {
next();
} else {
res.send('暗号错误')
}
}
app.get('/login', checkCodeMiddleware, (req, res) => {
res.send('login')
})
app.get('/user', checkCodeMiddleware, (req, res) => {
res.send('user')
})
app.get('*', (req, res) => {
res.send('404')
})
app.listen(9000, () => {
console.log('启动成功');
})
4.6 获取请求体数据 npm i body-parser
const express = require('express')
const bodyParser = require('body-parser')
const app = express()
// 解析JSON格式的请求体中间件
const jsonParser = bodyParser.json()
// 解析querystring格式请求体的中间件
const urlencodedParser = bodyParser.urlencoded();
app.get('/login', urlencodedParser, (req, res) => {
res.sendFile(__dirname + '/index.html')
})
app.post('/login', urlencodedParser, (req, res) => {
console.log(req.body); // 获取querystring数据
res.send('获取用户数据')
})
app.get('*', (req, res) => {
res.send('404')
})
app.listen(9000, () => {
console.log('启动成功');
})
4.7 使用express实现防盗链
const express = require('express')
const app = express()
// 必须先执行这个再执行下面的express.static, 顺序反了就出问题了不能实现防盗链
app.use((req, res, next) => {
const referer = req.get('referer');
console.log(referer, 'referer');
if (referer) {
const url = new URL(referer)
const {hostname} = url
if (hostname !== '127.0.0.1') {
return res.status(404).send('<h1>404</h1>')
}
}
next()
})
app.use(express.static(__dirname + '/public'))
app.get('/login', (req, res) => {
res.sendFile(__dirname + '/index.html')
})
app.post('/user', (req, res) => {
res.send('获取用户数据')
})
app.get('*', (req, res) => {
res.send('404')
})
app.listen(9000, () => {
console.log('启动成功');
})
4.8 路由的使用
// homeRouter.js
const express = require('express')
const router = express.Router();
// 首页相关路由
router.get('/login', (req, res) => {
res.send('login')
})
router.get('/search', (req, res) => {
res.send('search')
})
module.exports = router
// index.js
const homeRouter = require('./router/homeRouter')
const express = require('express')
const app = express()
app.use(homeRouter) // 使用中间件使用路由
app.get('/user', (req, res) => {
res.send('获取用户数据')
})
app.get('*', (req, res) => {
res.send('404')
})
app.listen(9000, () => {
console.log('启动成功');
})
5. MongoDB & mongoose
5.1 mongoose的使用
// mongoose的使用
// npm i mongoose
const mongoose = require('mongoose')
// 连接mongodb服务
mongoose.connect('mongodb://127.0.0.1:27017/bilibili')
// 设置相关回调
// 成功回调 (官方建议使用once而不是on)
mongoose.connection.once('open', () => {
console.log('连接成功');
})
// 失败回调
mongoose.connection.on('error', () => {
console.log('连接失败');
})
// 关闭回调
mongoose.connection.on('close', () => {
console.log('连接关闭');
})
setTimeout(() => {
mongoose.disconnect();
}, 2000);
5.2 对文档进行增删改查、个性化读取、数据排序、数据读取、文档字段类型、内置字段值验证
// 插入文档
const mongoose = require('mongoose')
mongoose.connect('mongodb://127.0.0.1:27017/bilibili')
mongoose.connection.once('open', () => {
console.log('连接成功');
// 创建文档的结构对象, 设置集合中文档的属性以及属性值的类型
const BookSchema = new mongoose.Schema({
name: String,
age: Number,
ids: Number,
add: String
})
// 创建模型对象, 对文档操作的封装对象
// mongoose.model() 第一个参数是集合名词, 第二个参数是结构对象
const BookModel = mongoose.model('books', BookSchema);
// 新增
// BookModel.create({
// name: '你猜',
// age: 444,
// ids: 333,
// add: 'ttt'
// }).then((data) => {
// console.log(data, 'data');
// }).catch((err) => {
// if (err) return console.log(err)
// })
// 删除单条和多条数据
// BookModel.deleteOne({
// name: '吴承恩'
// }).then((data) => {
// console.log(data, 'data');
// }).catch((err) => {
// if (err) return console.log(err)
// })
// BookModel.deleteMany({
// name: '吴承恩'
// }).then((data) => {
// console.log(data, 'data');
// }).catch((err) => {
// if (err) return console.log(err)
// })
// 更新单条文档和多条文档
// BookModel.updateOne({name: '吴承恩'}, {name: '吴承恩1'}).then((data) => {
// console.log(data, 'data');
// }).catch((err) => {
// if (err) return console.log(err)
// })
// BookModel.updateMany({name: '吴承恩1'}, {name: '吴承恩'}).then((data) => {
// console.log(data, 'data');
// }).catch((err) => {
// if (err) return console.log(err)
// })
// 读取单条和多条文档
// BookModel.findOne({name: '吴承恩'}).then((data) => {
// console.log(data, 'data');
// }).catch((err) => {
// if (err) return console.log(err)
// })
// BookModel.find({name: '吴承恩'}).then((data) => {
// console.log(data, 'data');
// }).catch((err) => {
// if (err) return console.log(err)
// })
// 设置返回字段, 查询到的数据只返回name和age, 其它不返回, _id设置后才能不显示, 值设置成1就行
// BookModel.find().select({name: 1, age: 1, _id: 0}).exec().then((data) => {
// console.log(data, 'data');
// }).catch((err) => {
// if (err) return console.log(err);
// })
// 数据排序, 1表示正序(升序), 2是倒序
// BookModel.find().sort({age: 1}).exec().then((data) => {
// console.log(data, 'data');
// }).catch((err) => {
// if (err) return console.log(err);
// })
// 数据的截取, 比如截取前三名
// BookModel.find().select({name: 1, age: 1, _id: 0}).sort({age: 1}).limit(3).exec().then((data) => {
// console.log(data, 'data');
// }).catch((err) => {
// if (err) return console.log(err);
// })
// 截取4 5 6名
BookModel.find().select({name: 1, age: 1, _id: 0}).sort({age: 1}).skip(3).limit(3).exec().then((data) => {
console.log(data, 'data');
}).catch((err) => {
if (err) return console.log(err);
})
})
// 内置字段值验证
const BookSchema = new mongoose.Schema({
name: {
type: String,
required: true, // 设置必填项
default: '默认值', // 设置默认值
enum: ['男', '女'], // 设置枚举值, 只能输入设置的这几个值之一
unique: true // 设置为独一无二的值, 必须重建集合才可以, 集合内的数据只能有一个值
},
})
//文档字段类型
// String 字符串
// Number 数字
// Boolean 布尔值
// Array 数组 也可以使用[]来标识
// Date 日期
// Buffer Buffer对象
// Mixed 任意类型,需要使用mongoose.Schema.Types.Mixed指定
// ObjectId 对象ID,需要使用mongoose.Schema.Types.ObjectId指定
// Decimal128 高精度数字,需要使用mongoose.Schema.Types.Decimal128指定
5.3 文档条件控制
// 文档条件控制
// 1. 运算符
// 在mongodb不能使用><!==等运算符, 需要使用替代符号
> 使用 $gt
< 使用 $lt
>= 使用 $gte
<= 使用 $lte
!= 使用 $ne
比如db.students.find({id: {$gt: 3}}) // 获取id比3大的所有记录
// 2. 逻辑运算
$or逻辑或的情况
db.student.find({$or: [{age: 18}, {age: 24}]}) // 查询年龄是18或者24的数据
$and逻辑与的情况
db.student.find({$and: [{age: {$lt: 20}}, {age: {$gt: 15}}]}) // 查询年龄大于15且小于20的数据
// 3. 正则匹配
db.students.find({name: /书籍/}) // 搜索名称含有书籍的数据
db.students.find({name: new RegExp('书籍')}) // 搜索名称含有书籍的数据
6. ejs
6.1 ejs的渲染使用
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1><%= china %></h1>
<h3><%= weather %></h3>
</body>
</html>
// index.js
const ejs = require('ejs')
const fs = require('fs')
const china = '中国'
const weather = '今天天气真不错'
const str = fs.readFileSync('./index.html').toString()
const result = ejs.render(str, {china, weather})
console.log(result);
6.2 ejs的列表渲染和条件渲染
// 列表渲染
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul>
<% list.forEach(item => {
%>
<li><%= item %></li>
<%
}) %>
</ul>
</body>
</html>
// index.js
const ejs = require('ejs')
const fs = require('fs')
const list = ['111', '222', '333', '444']
const html = fs.readFileSync('./index.html').toString()
const res = ejs.render(html, {list})
console.log(res);
// 条件渲染
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<% if (isLogin) {
%>
<span>isLogin是true</span>
<%
} else {
%>
<span>isLogin是false</span>
<%
} %>
</body>
</html>
// index.js
const ejs = require('ejs')
const fs = require('fs')
let isLogin = true
const html = fs.readFileSync('./index.html').toString();
const res = ejs.render(html, {isLogin})
console.log(res);
6.3 express中使用ejs
// index.js
const express = require('express')
const path = require('path')
const app = express()
// 设置模板引擎ejs
app.set('view engine', 'ejs')
// 设置模板文件存放位置 模板文件:具有模板语法内容的文件
app.set('views', path.resolve(__dirname, './views'))
app.get('/user', (req, res) => {
// render响应
const title = '111111111111'
res.render('home', {title})
})
app.get('*', (req, res) => {
res.send('404')
})
app.listen(9000, () => {
console.log('启动成功');
})
// view/home.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2><%= title %></h2>
</body>
</html>
6.4 express-generator工具(创建express+router+ejs等的脚手架)
// 安装
// npm i -g express-generator
// 把代码安装到哪个文件夹下
// express -e test