Vue+SpringBoot 实现用户头像上传(附前后端源码)

x33g5p2x  于2021-12-30 转载在 Vue.js  
字(7.4k)|赞(0)|评价(0)|浏览(1368)

一、背景

  • 后台系统一般会有用户个人信息的模块(见下图),为了增强用户的体验度,系统会开放自定义头像的功能,让用户可以上传自定义图片替代默认的系统头像。本文将通过Vue+SpringBoot来具体实现。

二、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">
                <myUpload

                  v-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>
...
  • 页面效果如下:

四、编写后台上传用户头像接口

  • 有了前端页面,就需要后台实现对应的接口,想一想,该接口需要有哪些功能呢?
  1. 需要将前端组件上传过来的头像文件保存到服务器上;
  2. 需要将保存在服务器上的用户头像路径存放到用户信息表中。

4.1 实现前端组件上传过来的头像文件保存到服务器上

  • 我们需要建立一个上传文件信息表,及建立上传文件的操作服务类来实现文件的保存。

  1. 上传文件信息表
/** * 文件信息表 * * @author zhuhuix * @date 2020-04-20 */
@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;

    @Override
    public String toString() {
        return "UploadFile{" +
                "fileName='" + fileName + '\'' +
                ", uploader='" + uploader + '\'' +
                ", createTime=" + createTime +
                '}';
    }
}
  1. 上传文件DAO
/** * 文件上传DAO接口 * * @author zhuhuix * @date 2021-07-19 */
@Mapper
public interface UploadFileMapper extends BaseMapper<UploadFile> {

}
  1. 上传文件工具具体实现类
/** * 文件上传接口定义 * * @author zhuhuix * @date 2020-04-20 */
public interface UploadFileTool {

    /** * 文件上传 * * @param uploader 上传人 * @param realName 文件实际名称 * @param multipartFile 文件 * @return 上传信息 */
   UploadFile upload(String uploader, String realName, MultipartFile multipartFile);
}
/** * 文件上传实现类 * * @author zhuhuix * @date 2020-04-20 */
@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());
        }

    }

    /** * 根据文件扩展名给文件类型 * * @param extension 文件扩展名 * @return 文件类型 */
    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 将保存在服务器上的用户头像路径存放到用户信息表中

  1. 在用户信息服务中增加保存头像信息的功能
/** * 用户信息 * * @author zhuhuix * @date 2020-04-03 */
public interface SysUserService {
	....
    /** * 修改用户头像 * @param file 文件 * @return json */
    Map<String,String>  updateAvatar(MultipartFile file);

}
/** * 用户信息实现 * * @author zhuhuix * @date 2020-04-03 */
@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("更新用户信息失败");
    }

    @Override
    public 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));
    }

五、前后端联调

六、源码

相关文章