Section-8 项目实战之上传图片模块
Lesson-1 上传图片需求分析
上传图片的需求场景
- 用户头像
- 封面图片
- 问题和回答中的图片
- 话题图片
上传图片的功能点
- 基础功能:上传图片、生成图片链接
- 附加功能:限制上传图片的大小与类型、生成高中低三种分辨率的图片链接,生成CDN(内容分发网络)
上传图片的技术方案
- 阿里云 OSS 等云服务,推荐在生产环境中使用
- 直接上传到服务器,不推荐在生产环境中使用
Lesson-2 使用 koa-body 中间件获取上传的文件
操作步骤
- 安装 koa-body,替换 koa-bodyparser
- 设置图片上传目录
- 使用 postman 上传文件
安装 koa-body,替换 koa-bodyparser
执行 npm i koa-body -S
卸载掉之前安装的 koa-bodyparser,执行 npm uninstall koa-bodyparser -S
,
设置图片上传目录
将代码中原来的bodyparser给替换掉,当前目录下(app文件夹)新增 public/uploads 文件夹,作为图片上传路径
// index.js
const koaBody = require('koa-body'); // 替换原本的 bodyparser
const path = require('path'); // 因为上传目录需要填写路径,所以需要用到 node 自带的 path 组件
app.use(koaBody({
multipart: true, // 启用文件
formidable: {
uploadDir: path.join(__dirname, 'public/uploads'), // 上传路径
keepExtensions: true // 保留拓展名
}
}));
我们将上传的方法写到 home.js 中,因为现在已经不是 users 特有的请求方法了
// controllers/home.js
class HomeCtl {
index (ctx) {
ctx.body = "这是主页";
}
// 新增 upload 方法
upload (ctx) {
const file = ctx.request.files.file;
ctx.body = {
path: file.path
};
}
}
// routes/home.js
const { index, upload } = require('../controllers/home');
// 路由中添加 post 方法,并规定路径为upload
router.post('/upload', upload);
使用 postman 上传文件
这里不废话了,直接按照下图设置,点body,key选择file,随便找一张图片点击send发生,成功将拿到上传的图片路径
![](https://img.haomeiwen.com/i13936697/ce78fd7fcf7b964e.png)
![](https://img.haomeiwen.com/i13936697/2adf6b97037ff062.png)
有玩王者并且有看虎牙的,应该认识这个主播吧,技术虽然菜,但颜值达到星秀级别,口头禅:哎哟欸,已经退网很久了,怀念她的小书包被一群人揍的情景,哈哈~
Lesson-3 使用 koa-static 中间件生成图片链接
操作步骤
- 安装 koa-static
- 设置静态文件目录
- 生成图片链接
koa-static
执行 npm i koa-static -S
进行安装
设置静态文件目录
// index.js
const koaStatic = require('koa-static');
// 使用koa-static,设置当前目录下的 public 文件夹作为上传图片的存储文件夹(静态资源文件夹)
app.use(koaStatic(path.join(__dirname, 'public')));
生成图片链接
这一步是要把上一节中图片的相对路径转化为可以直接通过http请求来访问的路径,那么需要拿到当前域名以及图片上传后的文件名,文件名需要借助node-path插件中的 basename() 方法,域名可以使用koa上下文环境中的 origin 属性(由于官网文档没锚点,请自行搜索),至于为什么可以直接使用 ctx.origin 而不是 ctx.request.origin,那是因为 request 有别名
![](https://img.haomeiwen.com/i13936697/e0fe7e9866c3a1c3.png)
// controllers/home.js
const path = require('path');
upload (ctx) {
const file = ctx.request.files.file;
const basename = path.basename(file.path);
ctx.body = {
url: `${ctx.origin}/uploads/${basename}` // ctx.origin -> localhost:3000
};
}
![](https://img.haomeiwen.com/i13936697/f54aab006113c4ef.png)
Lesson-4 编写前端页面上传文件
操作步骤
- 编写上传文件的前端页面
- 与后端接口联调测试
编写上传文件的前端页面
因为上一节我们已经使用了 koa-static 将 public 文件夹作为静态资源路径,koa-static 默认不指定文件的话,会直接去查找该文件夹下的 index.html 文件作为默认页,所以我们只需要在 public 下创建一个 uploads.html 文件,就能直接通过浏览器访问该文件,有兴趣可以阅读该简短文章 分析koa-static的源码
// public/uploads.html
<form action="/upload" enctype="multipart/form-data" method="POST">
<input type="file" name="file">
<button type="submit">上传</button>
</form>
与后端接口联调测试
这里其实就是直接测试上传文件是否成功,看一下文件夹中是否图片上传成功即可。因为我们是上传图片,所以需要对文件类型进行筛选,操作也很简单,只需要在 input 中指定一下 accept 属性即可,有三种格式,image/jpg, image/png || .jpg, .png || image/*,任君选择
<input type="file" name="file" accept="image/*">
Lesson-5 个人资料需求分析
浏览知乎个人资料页
这个就不演示了吧,直接去知乎进入个人资料页
个人资料功能点
- 不同类型(如字符串、数组)的属性
- 字段过滤
Lesson-6 个人资料的 schema 设计
操作步骤
- 分析个人资料的数据结构
- 设计个人资料的 schema
分析&设计 schema
// models/users.js
const mongoose = require('mongoose');
const { Schema, model } = mongoose;
const userSchema = new Schema({
__v: { type: Number, select: false },
name: { type: String, required: true },
password: { type: String, required: true, select: false },
avatar_url: { type: String }, // 用户头像
gender: { type: String, enum: ['male', 'female'], default: 'male', required: true }, // enum 可枚举,性别
headline: { type: String }, // 一句话简介
locations: { type: [{ type: String }] }, // 可枚举的字符串数组,居住地
business: { type: String }, // 公司
employments: { // 职业经历
type: [{
company: { type: String },
job: { type: String }
}]
},
educations: { // 教育经历
type: [{
school: { type: String },
major: { type: String },
diploma: { type: Number, enum: [1, 2, 3, 4, 5] },
entrance_year: { type: Number },
graduation_year: { type: Number }
}]
}
});
// user代表集合,导出的是一个类
module.exports = model('user', userSchema);