最近在开发一套前后端分离的企业管理系统时,需要对系统用户个人信息开放给系统用户自助修改更新,其中用户可以对自己用户名指定一个有个性的头像,这涉及到用户可以访问本地照片文件并进行预处理后上传到后端服务器保存起来,后续登录系统时能从后端数据取到头像图片展示在前端页面中。
对用户的头像管理需要满足以下几点关键需求:
(1)在前端能够预览图片、缩小、放大、截取等预处理操作。
(2)头像图片文件上传到后端服务器进行统一保存,图片文件名需要重新更改为无识别含义的随机文件名。
(3)将头像图片文件的位置保存在用户主文件数据库中,前端通过访问用户信息得到头像文件的url进行显示。
要实现以上需求,我们需要在Vue前端和Spring boot后端进行分别构建相应的功能程序。
一、Vue前端实现部分
本次前端头像图片处理及上传组件采用了 vue-image-crop-upload,该组件小巧简洁,且能支持多语言,但缺点是目前版本只支持PC端,但对于企业内部管理系统的后台管理功能基本也够用,后面有时间了再研究对手机端支持的问题。vue-image-crop-upload是完全开源的组件,我们可以根据自己的需要进行修改或增强此组件的功能。
我在前端通过在用户个人信息的卡片上放了一个“更改头像”的按钮,点击此按钮是将打开image-crop-upload组件进行选择图片文件并上传,上传成功后更新页面上的头像图片,同时根据后端返回的新头像文件名及url更新当前用户的后台数据库。为什么不在上传头像文件的同时就更新后台用户表的数据呢?我主要考虑是将后端图像文件上传的服务做得更加独立,让其可成为可复用的服务功能,因此将用户数据更新的处理交回前端来决定了,这样做不好的地方是前端与后端多交互一次。
vue页面代码:
......
......
import ImageCropper from '@/components/ImageCropper'
import { updateUserAvatar } from '@/api/permission/user'
export default {
components: { PanThumb, ImageCropper },
props: {
user: {
type: Object,
default: () => {
return {
username: '',
name: '',
phone: '',
email: '',
avatar: '',
introduction: '',
roles: ''
}
}
}
},
data() {
return {
imagecropperShow: false,
imagecropperKey: 0,
image: this.user.avatar
}
},
methods: {
cropSuccess(resData) {
this.imagecropperShow = false
this.imagecropperKey = this.imagecropperKey + 1
if (resData.fileUrlList && resData.fileUrlList.length > 0) {
this.user.avatar = resData.fileUrlList[0]
updateUserAvatar(this.user) // 更新用户的avatart
}
},
close() {
this.imagecropperShow = false
}
}
}
二、Spinrg boot后端实现部分
后端主要实现从前端页面接收到上传文件数据请求后,完成文件名的重构及保存在指定位置。
为了实现文件名去含义及保存,我单独封装了一个Upload类进行处理。
Upload.java
/**
* 上传文件到服务器本地静态资源文件位置
* @param file 上传保存的文件
* @param localFilePath 本地文件位置
*/
public static String uploadFile(MultipartFile file, String localFilePath) {
RandomUtility ru = new RandomUtility();
File f = new File(localFilePath, ru.getRandomString(12)
+ file.getOriginalFilename().substring(file.getOriginalFilename().length()-4));
if (!f.getParentFile().exists()) {
f.getParentFile().mkdir();
}
try {
file.transferTo(f); // 保存文件
return f.getName();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
注:RandomUtility是我封装的一个随机数产生器的类,这个比较简单,大家可以根据自己的习惯喜好来封装就好。
在Spring boot控制类中增加对接上传服务的api,获取到文件数据后保存到服务器的静态资源位置上。在正式的企业系统运行环境中,企业可能会单独一台或多台文件服务器进行保存相关文件资源,这就涉及到需要跨域上传文件,这个话题我们在后面再研究实现,本次只实现把文件保存在当前服务器的静态资源位置中。
XXXController.java
/**
* 上传用户头像图片文件
*
*/
@RequestMapping(value = "api/admin/user/uploadavatar", method=RequestMethod.POST)
public Result uploadUserAvatar(MultipartHttpServletRequest request, HttpServletResponse response) {
JSONObject jo = new JSONObject();
try {
Map
//获取项目classes/static的地址
String staticPath = ClassUtils.getDefaultClassLoader().getResource("static").getPath();
String imgLocalPath = staticPath + imgPath;
List
List
List
List
for(MultipartFile pic: files.values()){
try {
String fileName = Upload.uploadFile(pic, imgLocalPath);
String imgURL = uploadHost + imgPath + fileName;
messageList.add(pic.getName() + " Upload success!");
fileNameList.add(fileName); //文件名
fileUrlList.add(imgURL); //相对路径
realPathList.add(imgLocalPath); //真实完整路径
} catch (Exception e) {
e.printStackTrace();
messageList.add(e.getMessage());
fileNameList.add(pic.getName());
fileUrlList.add("");
realPathList.add("");
}
}
jo.put("resultMessage", messageList);
jo.put("fileNameList", fileNameList);
jo.put("fileUrlList", fileUrlList);
jo.put("realPathList", realPathList);
return ResultFactory.buildSuccessResult(jo);
}catch (Exception e) {
jo.put("resultMessage", e.getMessage());
return ResultFactory.buildFailResult(jo.toString());
}
}
对api的处理结果返回信息我采用了JSONObject来组织成json格式数据返回,如果不用这个方式,也可以自己定义一个结果返回类,通过实例化这个类并返回也会转为json格式返回给前端。
注: Result和ResultFactory是我自定义的接口结果类及结果处理机制类,大家可以根据自己的系统或项目要求进行决定是否统一封装。