vue+element+springboot实现多张图片上传

     1.需求说明
     2.实现思路
     3.el-upload组件主要属性说明
     4.前端传递MultipartFile数组与服务端接收说明
     5.完整代码

1.需求说明

    动态模块新增添加动态功能,支持多张图片上传.实现过程中对el-upload组件不是很熟悉,踩了很多坑,当然也参考过别的文章,发现处理很复杂.这里记录最终实现结果,方便有同样问题的同学查看,避免浪费多余时间.下面是发布动态的页面,点击上传图片打开本地文件选择,点击确定完成动态发布功能.
在这里插入图片描述

2.实现思路

    多张图片上传逻辑(兼容单张图片上传,只需要修改最大上传图片数量为1):
    1.需要页面选择好所有图片之后调用服务端的图片上传接口,这样处理的原因是图片上传过程中会出现几种场景:

选择好图片之后要删除之前选择的某一张或某几张;
选择好图片之后要追加几张图片

如果按照选择一张就调用一次图片上传接口,不但会增加服务端调用次数,而且还会增加无效图片的存储成本.
    2.多张图片一次上传接口调用成功后返回多张图片的图片地址,再调用服务端动态发布接口,完成动态发布功能

3.el-upload组件主要属性说明

1.禁用选择图片自动上传功能
    action属性:图片上传服务端请求地址,在组件中属于必传,默认选择一张图片就要调用一次.按照上面梳理的逻辑需要禁用调用该功能,auto-upload设置为false即可.action由于是必传,所以此处设置为#
2.开启多选
    设置multiple设置为true,默认false,否则在出现的文件选择窗口中只能选择一个文件.
3.设置选择文件最大数量
    使用limit属性,超过最大数量的处理逻辑可以在on-exceed中实现,其中处理的逻辑是页面提示已超过最大数请重新选择.
4.显示已选择的图片列表
    设置file-list实现
5.选择好图片之后追加几张图片问题处理
    on-change可以监听选中的图片,一次性选择多个图片会执行多次,但是为保证业务处理逻辑执行成功,只需要最后一次on-change中添加业务处理,所以通过判断监听返回的fileList集合长度是否是最大来处理.自定义的fileList就是on-change中最后一次on-change监听返回的fileList集合信息.下文中自定义的imgUrlList为调用文件上传服务端组装的图片参数集合.服务端的file对象对应on-change监听file中的file.raw.

handleChange(file, fileList){let length = fileList.length	this.maxLength = Math.max(length, this.maxLength)setTimeout(() => {if(length !== this.maxLength) {return} else {this.fileList=fileList}})}

6.选择好图片之后删除已选中图片问题处理
    before-remove可以监听要进行删除的图片信息.每个图片中有一个唯一标识uid,通过唯一标识删除自定义fileList中的图片

handleRemove(file, fileList){this.fileList=this.fileList.filter(imgFile=>imgFile.uid != file.uid)},

4.前端传递MultipartFile[]与服务端接收说明

    服务端接口为post请求,请求方式为post表单提交.具体如下

@PostMapping("/uploadImg")public ResultVo uploadImg(@RequestParam(name = "multipartFiles") MultipartFile[] multipartFiles,@RequestParam(name = "fileType") Integer fileType) {String url = adminDriftService.adminUploadImg(multipartFiles,fileType);return ResultVoUtil.success(url);}

前端页面需要按照FormData类型进行传递,注意一下参数拼接:

let formData = new FormData();this.imgUrlList.map(img=>{formData.append("multipartFiles", img)})formData.append("fileType", 2)

5.完整代码

    前端:
dynamic.js:

export function uploadImg(formData) {return axios({url: 'uploadImg',method: 'POST',	data: formData})
}

新增动态弹窗:

<el-dialog title="新增动态" :visible.sync="addDynamicVisible"><el-form :model="addDynamicForm"><el-form-item label="用户昵称" :label-width="formLabelWidth"><el-select v-model="addDynamicForm.userId" filterable placeholder="请选择"><el-optionv-for="systemUser in systemUserList":key="systemUser.userId":label="systemUser.userName":value="systemUser.userId"></el-option></el-select></el-form-item><el-form-item label="动态文本内容" :label-width="formLabelWidth"><el-input v-model="addDynamicForm.contentText" autocomplete="off"></el-input></el-form-item><el-form-item label="动态图片" :label-width="formLabelWidth"><el-uploadaction="#":file-list="fileList":before-remove="handleRemove":auto-upload="false" :multiple="true" :on-change="handleChange":limit="9":on-exceed="handleExceed"list-type="picture"><el-button size="small" type="primary">点击上传</el-button><div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div></el-upload></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button @click="closeAddDynamic()">取 消</el-button><el-button type="primary" @click="addDynamicInfo()">确 定</el-button></div></el-dialog><script>			
import {findSystemUserList,addDynamic,uploadImg} from "@/api/dynamic";
export default {data() {return {addDynamicVisible:false, // 是否显示新增动态弹窗addDynamicForm:{}, // 新增动态内容systemUserList:[],  // 系统用户列表信息formLabelWidth: '120px',imgUrlList:[], // 上传文件地址集合fileList:[], // 上传文件本地显示图片集合(非服务器图片地址)maxLength:0 // 上传文件最大值}}methods:{// 图片列表删除之后处理操作handleRemove(file, fileList){this.fileList=this.fileList.filter(imgFile=>imgFile.uid != file.uid)},	// 每次打开本地文件选择窗口选择图片后处理操作		handleChange(file, fileList){let length = fileList.length	this.maxLength = Math.max(length, this.maxLength)setTimeout(() => {if(length !== this.maxLength) {return} else {this.fileList=fileList}})},	// 选择图片超过最大限制9张之后处理操作handleExceed(files, fileList){this.msgError("最多选取9张,请重新选择!")// 清空之前选择内容this.fileList=[]},// 添加动态操作addDynamicInfo(){// 参数校验if(!this.addDynamicForm.userId){this.msgError("请选择用户!")return}this.imgUrlList=this.fileList.map(file=>file.raw)this.serverUploadImg()},	  // 上传图片处理逻辑serverUploadImg() {let formData = new FormData();this.imgUrlList.map(img=>{formData.append("multipartFiles", img)})formData.append("fileType", 2)uploadImg(formData).then(res => {if (res.code === 200) {// 图片上传成功之后触发动态发布逻辑this.addDynamicForm.contentImg=res.datathis.serverAddDynamic()}else {this.msgError(res.msg)}}).catch(() => {this.msgError("请求失败")})},	// 动态发布逻辑serverAddDynamic() {addDynamic(this.addDynamicForm).then(res => {if (res.code === 200) {this.msgSuccess('添加成功!');this.addDynamicVisible=false;this.addDynamicForm={};this.imgUrlList=[];this.fileList=[];this.serverFindDynamicInfoList(this.queryInfo)}else {this.msgError(res.msg)}}).catch(() => {this.msgError("请求失败")})},	  }
</script>		

服务端文件批量上传:

  @ApiImplicitParams({@ApiImplicitParam(name = "multipartFiles",value = "图片集合",required = true, dataType = "MultipartFile[]"),@ApiImplicitParam(name = "fileType",value = "文件类型:1.用户图片:头像以及背景图;2.动态图",required = true,dataType = "Integer",paramType = "insert",example = "1")})@ApiOperation("上传图片")@PostMapping("/uploadImg")public ResultVo uploadImg(@NotNull(message = "文件对象不允许为空!") @RequestParam(name = "multipartFiles") MultipartFile[] multipartFiles,@NotNull(message = "文件类型不允许为空!") @RequestParam(name = "fileType") Integer fileType) {String url = service.adminUploadImg(multipartFiles,fileType);return ResultVoUtil.success(url);}

多张文件上传实现逻辑:

   public String adminUploadImg(MultipartFile[] multipartFiles, Integer fileType) {String imgUrlStr ="";for (int i = 0; i < multipartFiles.length; i++) {String url = aliYunService.uploadImg(multipartFiles[i], fileType,"");imgUrlStr=imgUrlStr+url;if(multipartFiles.length > 1  && i < multipartFiles.length-1){imgUrlStr=imgUrlStr+",";}}return imgUrlStr;}

图片上传具体实现逻辑:

public String uploadImg(MultipartFile multipartFile,Integer fileType,String fileNameParam) {// 返回文件地址String fileUrl = "";// 文件校验checkFile(multipartFile,fileType);// 文件上传路径// modify by txm 2023/3/24 图片类型修改为枚举String filePath = FilePathEnum.getOSSFilePath(fileType);String fileName = StrUtil.isBlank(fileNameParam) ?  multipartFile.getOriginalFilename():fileNameParam;String pathKey = StrUtil.concat(true,filePath,"/",fileName);// meta设置请求头OSS ossClient = new OSSClientBuilder().build(driftConfig.getEndpoint(), driftConfig.getOssAccessKeyId(), driftConfig.getOssAccessKeySecret());try {// 上传至阿里OSSossClient.putObject(driftConfig.getBucketName(), pathKey, new ByteArrayInputStream(multipartFile.getBytes()));fileUrl = driftConfig.getUrlPrefix() + pathKey;} catch (Exception e) {// modify by txm 2023/10/16 修改描述log.error("文件上传失败:{}", e.getMessage());throw new BusinessException("文件上传失败:请重试!");} finally {if (ossClient != null) {ossClient.shutdown();}}// 上传成功return fileUrl;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/305626.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

配置代理解决跨域(CORS)问题

一、跨域 &#xff1f; 我们在完成前后端分离项目时&#xff08;VueSpringBoot&#xff09;&#xff0c;有很多人会遇到跨域问题&#xff08;CORS&#xff09;。 跨域&#xff08;Cross-Origin Resource Sharing&#xff0c;CORS&#xff09;是浏览器的一项安全功能&#xff…

【数据库】为什么要分库分表

为什么要分库分表 1.为什么要分库分表&#xff1f;2.垂直拆分3.水平拆分4.总结4.1 逻辑库和物理库4.2 逻辑表和物理表 1.为什么要分库分表&#xff1f; 随着近些年信息化大跃进&#xff0c;各行各业无纸化办公产生了大量的数据&#xff0c;而越来越多的数据存入了数据库中。当…

Leetcode—1572.矩阵对角线元素的和【简单】

2023每日刷题&#xff08;七十三&#xff09; Leetcode—1572.矩阵对角线元素的和 实现代码 class Solution { public:int diagonalSum(vector<vector<int>>& mat) {int n mat.size();if(n 1) {return mat[0][0];}int sum 0;int i 0, j n - 1;while(i &…

Leetcode 763 划分字母区间

题意理解&#xff1a; 要把这个字符串划分为尽可能多的片段&#xff0c;同一字母最多出现在一个片段中。 注意&#xff0c;划分结果需要满足&#xff1a;将所有划分结果按顺序连接&#xff0c;得到的字符串仍然是 s 。 返回一个表示每个字符串片段的长度的列表。 输入&#xff…

Java——值得收藏的Java final修饰符总结!!!

Java final修饰符总结 一、final修饰类二、final修饰方法三、final修饰变量 总结 算下刚转Java到现在也有三个多月了&#xff0c;所以打算对Java的知识进行汇总一下&#xff0c;本篇文章介绍一下Java的final修饰符的作用&#xff0c;final表示最后的、最终的含义&#xff0c;fi…

硅像素传感器文献调研(三)

写在前面&#xff1a; 引言&#xff1a;也是先总结前人的研究结果&#xff0c;重点论述其不足之处。 和该方向联系不大&#xff0c;但还是有值得学习的地方。逻辑很清晰&#xff0c;易读性很好。 1991年—场板半阻层 使用场板和半电阻层的高压平面器件 0.摘要 提出了一种…

没错,这条短视频烂尾了

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 “你这个太标题党了&#xff0c;后悔点进去了” “松哥&#xff0c;下次增加点故事情节” “你这是哪是沉浸式&#xff0c;你这是自嗨啊” “那你这拍了个寂寞” “这视频&#xff0c;开头可以&#xff0c;烂尾了” …

如何从 DSA 切换到 PMax 以使您的 Google 付费广告面向未来

为了在 Google Ads 不可避免的过渡期之前&#xff0c;我们将介绍如何从动态搜索广告切换到效果最大化广告 如何从 DSA 切换到 PMax 以使您的 Google 付费广告面向未来 变化是唯一不变的&#xff0c;尤其是在数字广告中——您可能听说过一些关于动态搜索广告 &#xff08;DSA&…

webstorm中直接运行ts(TypeScript)

参考&#xff1a;https://www.cnblogs.com/yangfanjie/p/12036118.html 1&#xff1a;安装ts: npm install -g typescript 2&#xff1a;安装直接运行所需依赖包&#xff1a; npm install -g ts-node 3&#xff1a;在设置中安装安装插件后重启 4&#xff1a;重启后就会发现在…

高效编写测试用例

本话题暂不探讨是否有必要编写详细的测试用例&#xff0c;在确定要交付详细的测试用例这个前提下&#xff0c;分享如何更高效地完成测试用例的编写。 对齐测试用例需求 首先、明确要完成的测试用例文档目标要求&#xff0c;模板、范围、粒度等。 用例文档使用者&#xff1a;…

github登录需要双因素认证(Two-factor authentication)

前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;神的孩子都在歌唱 github登录需要双因素认证&#xff08;Two-factor authentication&#xff09; 今天登录github发现需要绑定双因素才能够登录 我们…

提升设计效率:全面了解如何使用Figma插件

Figma组件库包括颜色、字体、图标、按钮、阴影、圆角、间距等。当Figma组件库的样式和Figma组件达到一定数量时&#xff0c;将难以维护&#xff0c;设计和开发的对接成本将大大提高。Figma可以在同一母版下单独设置样式&#xff0c;而不影响与母版之前的关系&#xff0c;这是Sk…