Springboot+vue实现图片上传与图片回显

2,190 阅读4分钟

开发思路

在做到这个需求的时候,首先想到的是模仿尚硅谷的谷粒学苑,使用oss存储,实现在数据库中能存储图片上传后的url这样是最方便的。

但是在开发过程中,发现获取到oss服务器存在困难,所以想到将图片上传功能改成简单的将图片保存到本地,数据库的字段存储的是保存图片的名称,再使前端动态渲染本地的图片即可

后端实现上传功能

在这里简单地实现了图片上传的功能

主要使用的就是java的IO流对图片的一系列操作,这样就可以将图片存储到本地

并且为了防止图片太大不能上传,要在springboot的配置文件中设置上传的最大大小

#单个数据的大小
spring.servlet.multipart.max-file-size = 100Mb
#总数据的大小
spring.servlet.multipart.max-request-size=100Mb

下方的java代码在swagger中测试的效果是

image.png

@RestController
@RequestMapping("/riskservice.MonitorData/patrolUpload")
@Api(description = "监测数据-巡视巡察数据-图片上传")
@CrossOrigin
@Slf4j
public class UploadPictureController {

//    https://blog.csdn.net/qq_41725313/article/details/124678465
    @PostMapping("/addCoverPic")
    public R addCoverPic(@RequestParam("file") MultipartFile upload){
        String filePath="C:/学校/刘老师任务/风险预警/2、项目源码/vue-admin-template-master/static/patrol_images/";
//        String filePath="D:/Template/picture";
        File file =new File(filePath);

        if(!file.exists()){
            file.mkdirs();
        }
        // 获取原始图片的扩展名
        String originalFileName = upload.getOriginalFilename();
        // 生成随机数防止图片重名
        String newFileName = UUID.randomUUID()+originalFileName;
        // 去掉随机生成的名字中的特殊字符
        newFileName = newFileName.replace("(","");
        newFileName = newFileName.replace(")","");
        // 设置图片的存储地址
        String newFilePath = filePath+newFileName;
        String newFileName2 = "/picture/"+newFileName;
        try {
            upload.transferTo(new File(newFilePath));
            //将传来的文件写入新建的文件
        }catch (IllegalStateException | IOException e) {
            //处理异常
        }
        log.info("图片保存的地址是",newFilePath);

        return R.ok().data("fileName", newFileName).data("filePath",filePath);
    }

}

前端实现上传以及回显

前端实现上传

index.vue,图片上传功能的组件 使用效果是

image.png

<template>
  <!--
  如果要改成自动上传的话
  action 要加上
  action="http://localhost:8081/riskservice.MonitorData/patrolUpload/addCoverPic"
  并且
  :auto-upload
  要改成
  :auto-upload="true"
 -->
  <el-upload
    class="custom_el_upload"
    :drag="drag"
    action=""
    :accept="accept.toString()"
    :limit="limit"
    :on-exceed="exceedHandle"
    :on-change="handleUploading"
    :on-remove="removeHandle"
    :file-list="fileList"
    :auto-upload="false"
    list-type="picture"
  >
    <template v-if="drag">
      <i class="el-icon-upload"></i>
      <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
    </template>
    <el-button size="small" type="primary" v-else>点击上传</el-button>

    <div class="el-upload__tip" slot="tip" v-if="intro">{{ intro }}</div>
    <div class="el-upload__tip" slot="tip" v-else>
      只能上传{{ accept.toString() }}文件,最多上传{{ limit }}个文件
    </div>
  </el-upload>
</template>

<script>
export default {
  props: {
    value: { default: "" },
    intro: { default: "" },
    limit: { type: Number, default: 1 },
    drag: { type: Boolean, default: true },
    accept: { type: Array, default: () => [".png", ".jpg", ".jpeg"] }
  },
  data() {
    return {
      fileList: []
    };
  },
  mounted() {},
  methods: {
    handleUploading(file, fileList) {
      this.updateList(file, fileList);
    },

    removeHandle(file, fileList) {
      this.updateList(file, fileList);
    },

    updateList(file, fileList) {
      if (!this.beforeUpload(file)) return; //上传文件不符要求

      const list = fileList.map(item => {
        return item.raw;
      });
      this.$emit("input", list);
    },

    //上传文件前检查文件类型和大小
    beforeUpload(file) {
      let type = file.name.substring(file.name.lastIndexOf("."));

      const isExcel = this.accept.includes(type);

      if (!isExcel) {
        this.$message.error(`上传文件只能是 ${this.accept.toString()} 格式!`);
      }

      if (!isExcel) this.fileList = [];

      return isExcel;
    },

    exceedHandle(files, fileList) {
      this.$message.error(`上传失败,您已经超出上传文件的数量限制`);
    }
  }
};
</script>

<style scoped lang="scss">
.custom_el_upload {
  ::v-deep .el-upload-dragger {
    height: 130px;
  }

  ::v-deep .el-icon-upload {
    margin-top: 20px;
  }
}
</style>

1、参数的意义 :

v-model="pictureFileUpload" : 上传后文件存放的数据,此数据为数组类型,定义为pictureFileUpload :accept="accept.toString()" :上传文件的指定类型,定义为accept :drag="true" : 是否可以拖动上传

		<el-form-item label="现场照片">
          <uploadForm     
            v-model="pictureFileUpload"
            :accept="accept.toString()"
            :drag="true"
          />
        </el-form-item>

2、参数定义

<script>
// 先在此处将我设置好的上传模板引用进来
import uploadForm from "./index.vue";
export default {
  props: {
      // accept上传文件的指定类型,这个直接复制粘贴即可
    accept: {
      type: Array,
      default: () => [".png", ".PNG", ".jpg", "JPG", ".jpeg", ".JPEG"]
    }
  },
  data() {
    return {
        // 最后上传完成的文件存放在这里
      pictureFileUpload: [], // 上传图片辅助数组
    };
  },
  components: {
    // 将import的上传模板注册为可以使用的组件,所以上方可以直接用<uploadForm />使用组件
    uploadForm
  },
}
</script>

3、上传完成后将文件传送回后端

// 先上传图片,得到图片存储的路径
console.log("此时添加的图片是", this.pictureFileUpload[0]);

// 此步需要将原本的数据类型转成FormData类型,后端才可以接收到数据
let formdata = new FormData();
formdata.append("file", this.pictureFileUpload[0]);
// 转换FormData类型完成

// 直接调用接口传递参数到后端
// 此处的参数就是上方的后端代码
await patrolApi.patrolUpload(formdata).then(res => {
	this.patrolInfo.photo = res.data.fileName;
    console.log("上传完成, 文件的位置是", this.patrolInfo.photo);
});

4、传递到后端的接口

patrolUpload(data) {
    return request({
        // 此处一定要修改请求头,加上下面这几行代码,要不然会传送失败
      headers: {
        "Content-Type": "multipart/form-data"
      },
        // 添加请求头结束
      url: `/riskservice.MonitorData/patrolUpload/addCoverPic`,
      method: 'post',
      data
    })
  }

图片回显讲解

由于文件是上传到项目中的static文件夹下,所以可以直接动态读取图片显示

1、数据封装

由于在数据库中存的只有文件名,方便发布之后修改代码,所以需要手动做路径的拼接

将图片的路径加上"/static/patrol_images/" 这样前端可以回显

getList() {
      patrolApi.getAllInfo().then(res => {
        this.list = res.data.list;
        for (var item in this.list) {
          this.list[item].photo = String(
            "/static/patrol_images/" + this.list[item].photo
          );
        }
        console.log("页面创建时获取到的数据是", this.list);
      });
    },

2、放在前端展示

数据回显直接读取文件即可显示加上vue注解 :src

注意,要让图片回显,路径一定要拼接正确,要是"/static/patrol_images/"+"正确的文件名"

<el-form-item label="现场照片">
          <el-image class="table-td-thumb" :src="patrolInfo.photo"></el-image>
    
        </el-form-item>

3、点击图片,使图片预览

在完成了上述操作后,应该就能使图片进行正常的回显,现在给图片组件添加上点击图片预览功能

1、首先给el-image组件加上:preview-src-lists属性使其能够进行预览

2、再给图片加上点击函数,使其点击后能触发预览

<el-image
            class="table-td-thumb"
            :src="scope.row.photo"
            :preview-src-list="srcList"
            @click="vbs(scope.row.photo)"
          ></el-image>

3、在data中定义存放预览图片的数组

data() {
    return {
      srcList: [] // 图片预览数组
    };
  },

4、编写点击后使图片预览的方法

methods: {
    // 点击图片后添加预览
    vbs(photo) {
      // 清除原本的预览
      this.srcList = [];
      this.srcList.push(photo);
    }
}

最后就能完成预览,实现的效果为

image.png 点击图片可实现图片预览

image.png

比较容易出现的问题

此部分容易出现文件名和文件路径的拼接问题,中间出现了任何一个错误图片都会上传失败或者不显示,所以需要仔细阅读代码,体会到流程,中间出现错误大概率是路径参数拼接的问题,请各位加断点耐心调试