前端代码
1. 首先我们需要用到elementUI的upload组件
<el-upload
class="avatar-uploader"
:show-file-list="false"
:http-request='uploadAvatar'
:before-upload="beforeAvatarUpload">
<img v-if="imageUrl" :src="imageUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
- http-request钩子函数:可以覆盖默认的上传行为,可以自定义上传的实现。
- brfore-upload钩子函数:在文件上传前进行操作,比如判断是否符合要求。
2. 实现brfore-upload钩子函数
beforeAvatarUpload(file) {
this.file = file
this.fileName = file.name
this.fileType = file.type
const isImage = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/gif'
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isImage) {
this.$message.error('上传头像图片只能是 JPG | PNG | GIF 格式!');
}
if (!isLt2M) {
this.$message.error('上传头像图片大小不能超过 2MB!');
}
return isImage && isLt2M;
},
- 如上图代码所示,我们规定了只能是三种格式,并且大小不能超过2MB
3. 实现http-reuqest钩子函数
uploadAvatar() {
// 使用formData进行文件上传
let formData = new FormData()
formData.append('fileName', this.fileName) // 文件名字
formData.append('file', this.file) // 当前文件
formData.append('name', this.$store.getters.userInfo.name) // 当前登录的用户的名字
formData.append('staff_num', this.getStaffNum) // 当前登录用户的员工编号,在数据库查找时会用到
formData.append('host', `//${window.location.hostname}:5000`) // 把当前主机名传过去,后端利于拼接并且存放在数据库
let url = 'http://localhost:5000/api/avatar/uploadAvatar'
let xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200) {
let res = JSON.parse(xhr.responseText)
this.$message.success(`${res.data.msg}`)
let dominName = window.location.hostname
this.getUser.avatar = `//${dominName}:5000${res.data.fileUrl}` // 这一步的目的是把vuex里存放的avatar的值也改变
let user = this.getUser
this.$store.dispatch('setUserInfo', user) // 更新vuex
setTimeout(() => {
location.reload() // 300ms之后,刷新页面。否则上传图像之后它不会自动改变
}, 300)
}
}
xhr.open('post', url)
xhr.setRequestHeader('Authorization', localStorage.jwtToken) // 由于后端使用了jwt验证。所以这里要设置一下请求头
xhr.send(formData)
总结一下:文件上传必须使用formData。请求方法可以用jQuery的ajax,也可以使用xmlHttpRequest。本文使用的就是xmlHttpRequest
后端代码
4. 安装forimidable来解析上传的文件
npm i forimidable --save
这时可能有同学会问,什么是forimidable?
formidable : Node.js模块,用于解析表单数据,特别是文件上传。
5. 解析图片
let form = new forimidable.IncomingForm()
form.uploadDir = path.join(__dirname, '../../public/avatars') // 设置静态资源路径
form.keepExtensions = true // 使用源文件的扩展名
form.parse(req, (err, fields, files) => {
if (err) return res.json(err)
let extName = '' // 文件后缀
switch(files.file.type) { // 这一步的目的是设置文件后缀
case 'image/jepg':
extName = 'jpg'
break
case 'image/png':
extName = 'png'
break
case 'image/gif':
extName = 'gif'
break
default:
extName = 'jpg'
break
}
let avatarName = `${fields.name}'s_avatar.${extName}` // 根据传过来的用户名以及上面获取到的后缀名拼接出新的头像的名字
let newPath = form.uploadDir + '/' + avatarName // 获取头像的路径
fs.rename(files.file.path, newPath, (err) => {
// 这一步是重命名,files.file.path是原始文件路径,newPath是上方拼接出来的。
// 同时,重命名还可以避免同一用户上传多个文件。占用服务器资源。
if (err) return res.json({data: {code: 400, msg: '操作失败'}})
// 接下来是将图片地址更新到数据库~
let update = {
avatar: `${fields.host}/avatars/${avatarName}`
}
// 把图片地址更新到数据库
User.findOneAndUpdate({name: fields.name, staff_num: fields.staff_num},
{$set: update}, {new: true}).then(user => {
res.json({data: {
code: 200,
msg: '操作成功',
}})
}).catch(err => res.json({data: {code: 500, msg: 'something error!'}}))
})
})
tips:将头像地址存放到数据库仅仅是更新一下。前端使用的还是更新的vuex里面的数据。 如图 ↓