开头
最近在做自己的项目,项目中有上传图片头像的功能,以前的时候我的图片都是存在后端的,如果把图片进行base64转码后存在后端数据库的话,数据库说不定就爆了,前段时间我了解到可以把图片存在云服务器上,经过我的多方学习,终于是搞完了全过程。
开发使用的是React
函数式组件配合Antd
组件库,然后后端是Node.js
的express
框架。
前端
我这里使用的是Antd的Upload
组件,然后使用customRequest
实现自定义上传逻辑,这边主要是为了配合项目封装的axios,如果不使用自定义上传逻辑,使用它自带的上传功能的话,封装的axios以及token相关的设置都要重新搞。
js
<Upload
name="avatar"
listType="picture-circle"
maxCount={1}
className="avatar-uploader"
showUploadList={false}
accept=".jpg,.png"
customRequest={handleUpload}
>
<img src={avatar} alt="" className="avatar-img" />
<div className="avatar-tip">
<span>点击上传头像</span>
</div>
</Upload>
关于Upload
的相关属性可以直接去antd的官网查看,这边主要是自定义方法的阐述。
使用Upload
组件选中文件点击确定,随后触发customRequest
调用handleUpload
方法,他接收一个参数对象如下:
js
{
"action": "",
"filename": "avatar",
"data": {},
"file": File
{
uid: "rc-upload-1717379196249-2"
lastModified: 1652845511820
lastModifiedDate: Wed May 18 2022 11:45:11 GMT+0800 (中国标准时间) {}
name: "UZBQRSR[5B@P~8)`7~B$VQK.jpg"
size: 359734
type: "image/jpeg"
webkitRelativePath: "",
}
"headers": {},
"withCredentials": false,
"method": "post"
}
我们主要关注的是对象里File类型的文件,随后在handleUpload
里定义一个formdata
,并使用append
方法把文件添加进去,随后调用接口将文件传到后端。
js
const handleUpload = async file => {
const form = new FormData()
form.append('file', file.file)
const res = await uploadAvatar(form)
setAvatar(res.avatar)
}
接口这里要注意,需要定义请求头为application/x-www-form-urlencoded; charset=UTF-8
,不然后端是收不到文件格式的文件的。
js
export const uploadAvatar = formData => {
return request.post('/users/info/avatar', formData, {
headers: {
// 上传文件需要改请求头
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
},
})
}
完整代码
js
// 组件.js:
import React, { useState, useEffect } from 'react'
import { uploadAvatar } from '@/apis/account'
import { Input, Upload } from 'antd'
const Comp = () =>{
const [avatar, setAvatar] = useState('')
// 自定义上传逻辑的方法
const handleUpload = async file => {
const form = new FormData()
form.append('file', file.file)
const res = await uploadAvatar(form)
setAvatar(res.avatar)
}
return (
<Upload
name="avatar"
listType="picture-circle"
maxCount={1}
className="avatar-uploader"
showUploadList={false}
accept=".jpg,.png"
customRequest={handleUpload}
>
<img src={avatar} alt="" className="avatar-img" />
<div className="avatar-tip">
<span>点击上传头像</span>
</div>
</Upload>
)
}
// 接口.js:
export const uploadAvatar = formData => {
return request.post('/users/info/avatar', formData, {
headers: {
// 上传文件需要改请求头
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
},
})
}
后端
现在文件已经通过请求传到后端了,后端首先需要一个中间件multer
用来解析请求中的文件:.any()
方法表示接受任意数量的上传字段,并将上传的文件信息存储在 req.files
中
js
const multer = require('multer') //处理文件上传的中间件
const upload = multer()
router.use(upload.any())
解析后获取的文件格式如下:
js
{
originalname: 'UZBQRSR[5B@P~8)`7~B$VQK.jpg',
encoding: '7bit',
mimetype: 'image/jpeg',
buffer: <Buffer ff d8 ff e1 00 8e 45 78 69 66 00 00 4d 4d 00 2a 00 00 00 08 00 05 01 00 00 03 00 00 00 01 00 00 00 00 0 00 01 01 00 03 00 00 00 01 00 00 00 00 87 69 00 04 ... 359684 more bytes>,
size: 359734
}
随后写接口:从req.files[0]
里拿到传过来的文件,随后调用阿里云的上传函数,将文件上传到阿里云OSS,uploadServer
方法的第一个参数是文件的名字,可以自己是设置,第二个参数是文件的buffer
属性,文件数据的缓冲区,是一个 Buffer
对象,包含了文件的实际内容,我们要上传的就是这个,阿里云上传完成后会拿到一个result
对象,随后就是拿到阿里云返回的url
保存到数据库以及返回到前端。
js
router.post('/api/users/info/avatar', async (req, res) => {
const file = req.files[0]
//上传到阿里云OSS
const result = await uploadServer(
'avatar-' + req.data.uid + '-' + Date.now() + '.jpg',
file.buffer
)
await User.updateOne({ uid: req.data.uid }, { $set: { avatar: result.url } }).catch(err => {
console.log(err)
})
res.status(200).send({
msg: '头像修改成功',
code: 2000,
data: {
avatar: result.url,
},
})
})
阿里云OSS
官网操作步骤如下
- 前往阿里云官网,点击右上角注册一个账号。
- 搜索
OSS
,点击搜索结果中的对象存储OSS,点击购买。 - 看需求购买,我是买的最便宜的
OSS资源包
+标准-本地冗余存储
+40G
+6个月
,才4米。 - 点击右上角控制台,在我的资源里找到对象存储OSS点进去。
- 点击左侧Bucket列表,随后创建Bucket,填写名称地区,我是开启公共读写,这个看需求。
- Hover头像点击AccessKey管理,创建AccessKey随后保存好(待会要用)
这样OSS存储服务器就弄好了。
代码
接下来编写之前用到的上传服务器的代码: 定义一个ali-oss.js
,剩下的看注释就好了。
js
const OSS = require('ali-oss')
const client = new OSS({
// 这里填写上一步创建的AccessKey
accessKeyId: '上一步创建的',
accessKeySecret: '上一步创建的',
// yourRegion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
region: 'oss-cn-hangzhou',
authorizationV4: true,
// yourBucketName填写Bucket名称。
bucket: '你的Bucket名称',
})
const uploadServer = async (ObjName, fileUrl) => {
try {
let result = await client.put(`AAA/${ObjName}`, fileUrl)
// AAA为文件夹, ObjName为文件名字,可以只写名字,就直接储存在 bucket 的根路径
// fileUrl就是要传的文件
return result
} catch (error) {
console.log(error)
}
}
module.exports = { uploadServer }
最后
这样整个流程就完成了,感觉还是有很多不懂的地方,还要多多学习。
这里贴一下阿里云官方关于Node.js上传文件的文档:Node.js文件上传