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-option
					      v-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-upload
					action="#"
				    :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.data
						this.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 {
            // 上传至阿里OSS
            ossClient.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;
    }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用c语言实现DH秘钥分配算法

使用c语言实现DH秘钥分配算法 DH算法原理 密钥分配 选择一个大素数p&#xff0c; 选择一个整数g(g < p)&#xff1b;通信方A选择一个随机数a&#xff0c;并发送 mod p 给 通信方B&#xff1b;通信方B选择一个随机数b&#xff0c;并发送 mod p 给 通信方A&#xff1b;通信…

日常中msvcp120.dll丢失五种解决方法

在日常使用电脑的过程中&#xff0c;我们可能会遇到一些错误提示&#xff0c;其中之一就是“msvcp120.dll丢失”。那么&#xff0c;msvcp120.dll到底是什么&#xff1f;它的作用又是什么呢&#xff1f;为什么会出现丢失的情况呢&#xff1f;本文将为您详细介绍msvcp120.dll的相…

数据结构初阶之顺序表(C语言实现)

数据结构初阶之线性表&#xff08;C语言实现&#xff09; &#x1f34f;前言&#xff1a;&#x1f34f;顺序表和数组的区别&#x1f34f;动态顺序表的模拟实现&#x1f340;动态顺序表的基本结构设计&#x1f340;动态顺序表的各种功能模拟实现&#x1f432; 初始化&#xff08…

兔子目标检测数据集VOC格式3900张

兔子是一类可爱的哺乳动物&#xff0c;拥有圆润的脸庞和长长的耳朵&#xff0c;身体轻盈柔软。它们通常是以温和和友善的形象出现在人们的视野中&#xff0c;因此常常成为童话故事和卡通形象中的角色。 兔子是草食性动物&#xff0c;主要以各种草本植物为食&#xff0c;包括草…

【八】【C语言\动态规划】1567. 乘积为正数的最长子数组长度、413. 等差数列划分、978. 最长湍流子数组,三道题目深度解析

动态规划 动态规划就像是解决问题的一种策略&#xff0c;它可以帮助我们更高效地找到问题的解决方案。这个策略的核心思想就是将问题分解为一系列的小问题&#xff0c;并将每个小问题的解保存起来。这样&#xff0c;当我们需要解决原始问题的时候&#xff0c;我们就可以直接利…

git 学习 之一个规范的 commit 如何写

最好的话做一件完整的事情就提交一次

状态管理概述

ArkTS UI的状态管理到这里就叙述完了&#xff0c;现在做一个概述&#xff0c;也可以认为是一个总结。 在声明式UI编程框架中&#xff0c;UI是程序状态的运行结果&#xff0c;用户构建了一个UI模型&#xff0c;其中应用的运行时的状态是参数。当参数改变时&#xff0c;UI作为返回…

Vue(一):Vue 入门与 Vue 指令

Vue 01. Vue 快速上手 1.1 Vue 的基本概念 用于 构建用户界面 的 渐进性 框架 构建用户界面&#xff1a;基于数据去渲染用户看到的界面渐进式&#xff1a;不需要学习全部的语法就能完成一些功能&#xff0c;学习是循序渐进的框架&#xff1a;一套完整的项目解决方案&#x…

探索Apache Commons Imaging处理图像

第1章&#xff1a;引言 大家好&#xff0c;我是小黑&#xff0c;咱们今天来聊聊图像处理。在这个数字化日益增长的时代&#xff0c;图像处理已经成为了一个不可或缺的技能。不论是社交媒体上的照片编辑&#xff0c;还是专业领域的图像分析&#xff0c;图像处理无处不在。而作为…

JavaSE50题:26. (数组练习题)使奇数位于偶数之前

概述 调整数组顺序使得奇数位于偶数之前&#xff0c;调整之后&#xff0c;不关心大小顺序。 如数组&#xff1a;{1,2,3,4,5,6} 调整后可能是&#xff1a;{1&#xff0c;5&#xff0c;3&#xff0c;4&#xff0c;2&#xff0c;6} 方法 定义 left 和 right&#xff0c;二者分别…

位运算|比特位计数、汉明距离

位运算|比特位计数、汉明距离 338 比特位计数 /** 比特位计数法一&#xff1a;Brian Kernighan 算法的原理是&#xff1a;对于任意整数 x&#xff0c;令 xx & (x−1)&#xff0c;该运算将 x 的二进制表示的最后一个 1 变成 0。因此&#xff0c;对 x 重复该操作&#xff0…

中职网络安全Web2003-2——Web渗透测试

需要环境或换&#xff0c;有问题可以私信我或加Q 1.通过URL访问http://靶机IP/1&#xff0c;对该页面进行渗透测试&#xff0c;将完成后返回的结果内容作为Flag值提交&#xff1b; FLAGflag{htmlcode} 2.通过URL访问http://靶机IP/2&#xff0c;对该页面进行渗透测试&#xff…

数字钥匙进入3.0时代,他们要做智能汽车时代的「微信」

“假设用我们的即时通讯的工具来说&#xff0c;我们想造一个微信&#xff0c;我们希望跟更多的主机厂拥抱融合&#xff0c;而不是造一个飞信。” 11月24日&#xff0c;在银基科技承办的第三届数字钥匙及生态大会期间&#xff0c;银基科技CEO单宏寅尝试向到场的媒体&#xff0c…

Flink Kafka[输入/输出] Connector

本章重点介绍生产环境中最常用到的Flink kafka connector。使用Flink的同学&#xff0c;一定会很熟悉kafka&#xff0c;它是一个分布式的、分区的、多副本的、 支持高吞吐的、发布订阅消息系统。生产环境环境中也经常会跟kafka进行一些数据的交换&#xff0c;比如利用kafka con…

代码随想录刷题题Day24

刷题的第二十四天&#xff0c;希望自己能够不断坚持下去&#xff0c;迎来蜕变。&#x1f600;&#x1f600;&#x1f600; 刷题语言&#xff1a;C Day24 任务 ● 491.递增子序列 ● 46.全排列 ● 47.全排列 II 1 递增子序列 491.递增子序列 思路&#xff1a; 本题求自增子序…

Translation翻译插件

Translation插件是为IntelliJ IDEA开发的&#xff0c;因此只能在IntelliJ IDEA中使用。但是&#xff0c;如果你需要在其他软件中进行翻译&#xff0c;可以考虑使用其他的翻译工具或服务。例如&#xff0c;一些在线翻译网站&#xff08;如Google翻译、百度翻译等&#xff09;提供…

由浅入深走进Python异步编程【协程与yield】(含代码实例讲解 || 迭代器、生成器、协程、yield from)

写在前面 从底层到第三方库&#xff0c;全面讲解python的异步编程。这节讲述的是python异步编程的底层原理第一节&#xff0c;详细了解需要配合下一节观看哦。纯干货&#xff0c;无概念&#xff0c;代码实例讲解。 本系列有6章左右&#xff0c;点击头像或者专栏查看更多内容&…

C++学习实践(一)高频面试问题总结(附详细答案)

文章目录 一、基础常见面试题1、数组和链表区别2、深拷贝和浅拷贝相关问题的区别3、a和a区别4、c内存模型5、四种强制转换和应用场景 二、指针相关1、指针和引用的区别2、函数指针和指针函数3、传指针、引用和值4、常量指针和指针常量5、野指针6、智能指针的用法 三、关键字作用…

YOLOv8可视化:引入多种可视化CAM方法,为科研保驾护航

💡💡💡本文内容:调用pytorch下的CAM可视化库,支持十多种可视化方法,打开“黑盒”,让YOLOv8变得相对可解释性 收录 YOLOv8原创自研 https://blog.csdn.net/m0_63774211/category_12511737.html?spm=1001.2014.3001.5482 💡💡💡全网独家首发创新(原创),适…

桥接模式-举例

概叙&#xff1a;桥接模式用一种巧妙的方式处理多层继承存在的问题&#xff0c; 用抽象关联取代了传统的多层继承&#xff0c; 将类之间的静态继承关系转换为动态的对象组合关系&#xff0c; 使得系统更加灵活&#xff0c;并易于扩展&#xff0c; 同时有效控制了系统中类的个数…