文章目录
- 一、背景
- 二、vue-image-crop-upload组件
- 三、编写前端上传头像功能
- 四、编写后台上传用户头像接口
- 4.1 实现前端组件上传过来的头像文件保存到服务器上
- 4.2 将保存在服务器上的用户头像路径存放到用户信息表中
- 4.3 编写用户更新头像信息API
- 五、前后端联调
- 六、源码
一、背景
- 后台系统一般会有用户个人信息的模块(见下图),为了增强用户的体验度,系统会开放自定义头像的功能,让用户可以上传自定义图片替代默认的系统头像。本文将通过Vue+SpringBoot来具体实现。

二、vue-image-crop-upload组件
- 首先我们会用到vue-image-crop-upload组件

- 一些重要的参数与事件


三、编写前端上传头像功能
- 了解了vue-image-crop-upload组件的功能后,编写自定义头像前台上传功能就相当容易了,我们只需要把这个组件嵌入到个人信息页面中,并编写相应事件即可。
<template><div class="app-container">...<div slot="header" class="clearfix"><span>个人信息</span></div><div><div style="text-align: center"><div class="el-upload"><myUploadv-model="showDialog":headers="headers":url="baseApi+'/api/user/updateAvatar'"@crop-upload-success="cropUploadSuccess"/><img:src="user.avatarUrl? baseApi + '/file/' + user.avatarUrl: Avatar"title="点击上传头像"class="avatar"@click="toggleShow">...</div></div></div>..</div>
</template><script>
import myUpload from 'vue-image-crop-upload'
import { mapGetters } from 'vuex'
import { getToken } from '@/utils/auth'
import store from '@/store'
import Avatar from '@/assets/images/avatar.png'
export default {name: 'Center',components: { myUpload },data() {return {showDialog: false,Avatar: Avatar,headers: {'Authorization': getToken()},...}},computed: {...mapGetters(['user','baseApi'])},methods: {toggleShow() {this.showDialog = !this.showDialog},cropUploadSuccess(jsonData, field) {store.dispatch('getInfo').then(() => { })},...}
}
</script>
...
- 页面效果如下:

四、编写后台上传用户头像接口
- 有了前端页面,就需要后台实现对应的接口,想一想,该接口需要有哪些功能呢?
- 需要将前端组件上传过来的头像文件保存到服务器上;
- 需要将保存在服务器上的用户头像路径存放到用户信息表中。
4.1 实现前端组件上传过来的头像文件保存到服务器上
- 我们需要建立一个上传文件信息表,及建立上传文件的操作服务类来实现文件的保存。

- 上传文件信息表
@ApiModel(value = "用户信息")
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@TableName("upload_file")
public class UploadFile implements Serializable {@TableId(value = "id", type = IdType.AUTO)private Long id;private String realName;private String fileName;private String primaryName;private String extension;private String path;private String type;private Long size;private String uploader;private Timestamp createTime;@Overridepublic String toString() {return "UploadFile{" +"fileName='" + fileName + '\'' +", uploader='" + uploader + '\'' +", createTime=" + createTime +'}';}
}
- 上传文件DAO
@Mapper
public interface UploadFileMapper extends BaseMapper<UploadFile> {}
- 上传文件工具具体实现类
public interface UploadFileTool {UploadFile upload(String uploader, String realName, MultipartFile multipartFile);
}
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class UploadFileToolImpl implements UploadFileTool {private final UploadFileMapper uploadFileMapper;@Value("${uploadFile.path}")private String path;@Value("${uploadFile.maxSize}")private long maxSize;@Override@Transactional(rollbackFor = Exception.class)public UploadFile upload(String uploader, String realName, MultipartFile multipartFile) {if (multipartFile.getSize() > maxSize * Constant.MB) {throw new RuntimeException("超出文件上传大小限制" + maxSize + "MB");}String primaryName = FileUtil.mainName(multipartFile.getOriginalFilename());String extension = FileUtil.extName(multipartFile.getOriginalFilename());String type = getFileType(extension);LocalDateTime date = LocalDateTime.now();DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyyMMddhhmmssS");String nowStr = "-" + date.format(format);String fileName = primaryName + nowStr + "." + extension;try {String filePath = path + type + File.separator + fileName;File dest = new File(filePath).getCanonicalFile();if (!dest.getParentFile().exists()) {if (ObjectUtil.isNull(dest.getParentFile().mkdirs())) {throw new RuntimeException("上传文件失败:建立目录错误");}}multipartFile.transferTo(dest);if (ObjectUtil.isNull(dest)) {throw new RuntimeException("上传文件失败");}UploadFile uploadFile = new UploadFile(null, realName, fileName, primaryName,extension, dest.getPath(), type, multipartFile.getSize(),uploader, Timestamp.valueOf(LocalDateTime.now()));if (uploadFileMapper.insert(uploadFile) > 0) {return uploadFile;}throw new RuntimeException("上传文件失败");} catch (Exception e) {e.printStackTrace();throw new RuntimeException(e.getMessage());}}private static String getFileType(String extension) {String document = "txt doc pdf ppt pps xlsx xls docx csv";String music = "mp3 wav wma mpa ram ra aac aif m4a";String video = "avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg";String image = "bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg";if (image.contains(extension)) {return "image";} else if (document.contains(extension)) {return "document";} else if (music.contains(extension)) {return "music";} else if (video.contains(extension)) {return "video";} else {return "other";}}
}
4.2 将保存在服务器上的用户头像路径存放到用户信息表中
- 在用户信息服务中增加保存头像信息的功能
public interface SysUserService {....Map<String,String> updateAvatar(MultipartFile file);}
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class SysUserServiceImpl implements SysUserService {private final SysUserMapper sysUserMapper;private final UploadFileTool uploadFileTool;@Override@Transactional(rollbackFor = Exception.class)public Map<String, String> updateAvatar(MultipartFile file) {SysUser sysUser = findByUserName(getCurrentLoginUserName());UploadFile uploadFile = uploadFileTool.upload(sysUser.getUserName(), file.getOriginalFilename(), file);sysUser.setAvatarUrl(uploadFile.getType() + File.separator + uploadFile.getFileName());update(sysUser);return new HashMap<String, String>(1) {{put("avatar", uploadFile.getFileName());}};}@Override@Transactional(rollbackFor = Exception.class)public SysUser update(SysUser user) {if (sysUserMapper.updateById(user) > 0) {return user;}throw new RuntimeException("更新用户信息失败");}@Overridepublic SysUser findByUserName(String userName) {return sysUserMapper.selectOne(new QueryWrapper<SysUser>().lambda().eq(SysUser::getUserName, userName));}private String getCurrentLoginUserName() {final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();if (authentication == null) {throw new RuntimeException("登录状态已过期");}if (authentication.getPrincipal() instanceof UserDetails) {UserDetails userDetails = (UserDetails) authentication.getPrincipal();return (userDetails.getUsername());}throw new RuntimeException("找不到当前登录的信息");}}
4.3 编写用户更新头像信息API
- 最后只需要在Controller层增加用户更新头像信息API接口供前端调用即可
@ApiOperation("修改用户头像")@PostMapping(value = "/updateAvatar")public ResponseEntity<Object> updateAvatar(@RequestParam MultipartFile avatar) {return ResponseEntity.ok(sysUserService.updateAvatar(avatar));}

五、前后端联调

六、源码
- 前端
https://gitee.com/zhuhuix/startup-frontend
https://github.com/zhuhuix/startup-frontend - 后端
https://gitee.com/zhuhuix/startup-backend
https://github.com/zhuhuix/startup-backend