SpringBoot+Vue 实现头像上传功能

效果图

引入图片处理插件

npm install vue-cropper

前端

<template>
  <!-- 添加编辑窗口 -->
  <drag-modal :visible="visible" :showBtn="false" :title="this.$t('title')" :width="650" :bodyStyle="'height:453px;overflow:auto;padding:3px;'" @cancel="close">
    <a-spin :tip="this.$t('dataProcessing')" :spinning="loading">
      <a-row>
        <a-col :span="12" :style="{height: '350px'}">
          <vue-cropper ref="cropper" :img="options.img" :info="true" :autoCrop="options.autoCrop" :autoCropWidth="options.autoCropWidth" :autoCropHeight="options.autoCropHeight" :fixedBox="options.fixedBox" @realTime="realTime">
          </vue-cropper>
        </a-col>
        <a-col :span="12" :style="{height: '350px'}">
          <div class="avatar-upload-preview">
            <img :src="previews.url" :style="previews.img" />
          </div>
        </a-col>
      </a-row>
      <br />
      <a-row>
        <a-col :span="12">
          <a-upload name="file" accept=".png,.jpg,.jpeg" :beforeUpload="beforeUpload" :showUploadList="false">
            <a-button icon="upload">{{ $t('choice') }}</a-button>
          </a-upload>

          <a-button icon="plus" @click="changeScale(1)" />

          <a-button icon="minus" @click="changeScale(-1)" />

          <a-button icon="undo" @click="rotateLeft" />

          <a-button icon="redo" @click="rotateRight" />
        </a-col>
        <a-col :span="12" style="text-align:center;">
          <a-button :disabled="!haveFile" type="primary" @click="save()">{{ $t('save') }}</a-button>
        </a-col>
      </a-row>
    </a-spin>
  </drag-modal>
</template>
<script>
//弹窗可拖动插件
import dragModal from "@/components/dialog/dragModal";
//图片处理插件
import { VueCropper } from 'vue-cropper'
import { uploadAvatar } from "@/api/system/sys_user";
import { setLoginUser } from "@/utils/util";

export default {
  name: "avatar-dialog",
  i18n: require("./i18n"),
  components: { dragModal, VueCropper },
  data() {
    return {
      loading: false,
      visible: false,
      haveFile: false,
      options: {
        img: '',
        autoCrop: true,
        autoCropWidth: 200,
        autoCropHeight: 200,
        fixedBox: true
      },
      previews: {}
    };
  },
  props: {},
  methods: {
    //打开
    open() {
      this.visible = true;
    },
    //关闭
    close() {
      this.visible = false;
    },
    rotateLeft() {
      this.$refs.cropper.rotateLeft()
    },
    rotateRight() {
      this.$refs.cropper.rotateRight()
    },
    changeScale(num) {
      num = num || 1
      this.$refs.cropper.changeScale(num)
    },
    beforeUpload(file) {
      this.haveFile = false;
      if (file.name.lastIndexOf(".png") > 0
        || file.name.lastIndexOf(".jpg") > 0
        || file.name.lastIndexOf(".jpeg") > 0) {
        this.haveFile = true;
      } else {
        this.options.img = "";
        this.$message.error(this.$t("msg.msg001"));
        return;
      }

      const reader = new FileReader()
      // 把Array Buffer转化为blob 如果是base64不需要
      // 转化为base64
      reader.readAsDataURL(file)
      reader.onload = () => {
        this.options.img = reader.result;
      }
      // 转化为blob
      // reader.readAsArrayBuffer(file)
      return false
    },
    // 上传图片(点击上传按钮)
    save() {
      this.loading = true;
      const _this = this
      const formData = new FormData()
      this.$refs.cropper.getCropBlob((data) => {
        const img = window.URL.createObjectURL(data)
        this.model = true
        this.modelSrc = img
        formData.append('avatarfile', data, this.fileName)
        uploadAvatar(formData).then(res => {
          if (res.code == 200) {
            setLoginUser(res.data);
            //this.$message.success("保存成功");
            this.$message.success(this.$t("msg.msg002"));
            _this.visible = false
            this.$emit("ok");
          } else {
            //this.$message.success("保存失败");
            this.$message.error(this.$t("msg.msg003") + res.code);
          }
        }).finally(() => {
          this.loading = false;
        });
      });
    },
    realTime(data) {
      this.previews = data
    }
  },
};
</script>
<style lang="less" scoped>
.avatar-upload-preview {
  position: absolute;
  top: 50%;
  left: -5%;
  transform: translate(50%, -50%);
  width: 180px;
  height: 180px;
  border-radius: 50%;
  box-shadow: 0 0 4px #ccc;
  overflow: hidden;

  img {
    width: 100%;
    height: 100%;
  }
}
</style>

后台

/**
     * 保存头像
     */
    @Log(title = "用户信息", businessType = BusinessType.UPDATE)
    @PostMapping("/updateAvatar")
    @PreAuthorize("hasAuthority('sys:app:all')")
    @ResponseBody
    public HttpResult updateAvatar(@RequestParam("avatarfile") MultipartFile file)
    {
        String userId = getUserId();
        try
        {
            if (!file.isEmpty())
            {
                SysUser user = service.selectById(Long.parseLong(userId));
                String avatar = FileUploadUtils.upload(ProjectConfig.getAvatarPath(), file);
                user.setAvatar(avatar);
                if (service.update(user) > 0)
                {
                    user.clearSensitiveField();
                    return HttpResult.ok(user);
                }
                return HttpResult.error(10001,"图片上传失败");
            }
            return HttpResult.error(10002,"未找到要上传图片");
        }
        catch (Exception e)
        {
            return HttpResult.error(10003,e.getMessage());
        }
    }

1. 前端页面 在前端页面中,使用ElementUI组件实现上传头像功能。首先需要引入ElementUI组件库和axios库,用于发送请求。然后在Vue实例中定义一个data对象,用于存储上传头像的相关信息。 ```html <template> <div> <el-upload class="avatar-uploader" action="/api/upload" :show-file-list="false" :on-success="handleUploadSuccess" :before-upload="beforeAvatarUpload" > <img v-if="imageUrl" :src="imageUrl" class="avatar"> <i v-else class="el-icon-plus avatar-uploader-icon"></i> </el-upload> </div> </template> <script> import axios from 'axios' import { Message } from 'element-ui' export default { data() { return { imageUrl: '', uploadUrl: '/api/upload' } }, methods: { beforeAvatarUpload(file) { const isJPG = file.type === 'image/jpeg' || file.type === 'image/png' if (!isJPG) { Message.error('上传头像图片只能是 JPG 或 PNG 格式!') } const isLt2M = file.size / 1024 / 1024 < 2 if (!isLt2M) { Message.error('上传头像图片大小不能超过 2MB!') } return isJPG && isLt2M }, handleUploadSuccess(response, file) { this.imageUrl = URL.createObjectURL(file.raw) this.$emit('update:avatar', this.imageUrl) } } } </script> ``` 上述代码中,使用了`<el-upload>`组件实现上传头像功能。其中,`action`属性指定了上传头像的接口地址,`show-file-list`属性为false表示不显示上传文件列表,`on-success`属性指定了上传成功后的回调函数,`before-upload`属性指定了上传前的校验函数。 在`beforeAvatarUpload`函数中,对上传的文件进行格式和大小的校验,如果不符合要求则提示用户。如果校验通过,则返回true,表示可以上传。 在`handleUploadSuccess`函数中,获取上传成功后服务器返回的图片地址,将其赋值给`imageUrl`变量,然后使用`URL.createObjectURL`方法创建一个临时的Object URL,将其赋值给`imageUrl`变量,用于在页面中显示头像。 2. 后端接口 在后端接口中,使用SpringBoot框架实现上传头像功能。首先需要在Controller中添加一个接口,用于接收上传头像文件。 ```java @RestController @RequestMapping("/api") public class UploadController { @Autowired private Environment env; @PostMapping("/upload") public Map<String, Object> upload(MultipartFile file) throws Exception { String fileName = UUID.randomUUID().toString() + "." + FilenameUtils.getExtension(file.getOriginalFilename()); String filePath = env.getProperty("spring.servlet.multipart.location") + fileName; File dest = new File(filePath); file.transferTo(dest); Map<String, Object> map = new HashMap<>(); map.put("url", "/uploads/" + fileName); return map; } } ``` 上述代码中,使用了`@PostMapping`注解指定了上传头像的接口地址为`/api/upload`,使用了`@Autowired`注解注入了`Environment`对象,用于获取上传文件的保存路径。在`upload`方法中,首先生成一个随机的文件名,然后将上传的文件保存到指定的路径中。 最后将上传成功后的文件路径返回给前端页面。 3. 图片显示 通过前面的步骤,已经实现上传头像功能,并将上传成功后的图片路径返回给前端页面。现在需要将图片显示在页面中。 在Vue实例中,使用`imageUrl`变量存储上传成功后的图片路径。然后将该变量绑定到`<img>`标签的`src`属性上,用于显示上传头像。 ```html <img v-if="imageUrl" :src="imageUrl" class="avatar"> ``` 如果上传头像图片比较大,可能会造成页面加载缓慢,影响用户体验。为了提高页面加载速度,可以使用懒加载的方式加载图片,只有当图片进入可视区域时才进行加载。 Vue提供了`v-lazy`指令,可以实现图片的懒加载。只需要将`v-lazy`指令绑定到`<img>`标签的`src`属性上,然后在`<img>`标签上添加一个`lazy`类,就可以实现图片的懒加载。 ```html <img v-if="imageUrl" v-lazy="imageUrl" class="avatar lazy"> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值