多张图片上传、图片回显、url路径转成File文件

1. 实现

背景:在表单中使用element-plus实现多张图片上传(限制最多10张),因为还要与其他参数一起上传,所以使用formData格式。
编辑表单回显时得到的是图片路径数组,上传的格式是File,所以要进行一次转换。
在这里插入图片描述

<template><el-dialog v-model="visible" :title="`${props.type === 'add' ? '新增' : '编辑'}`" direction="rtl" @close="handleDialogClose":close-on-click-modal="false" class="auto-dialog" :center="true" destroy-on-close><el-form ref="ruleFormRef" :model="ruleForm" label-position="right" label-width="auto"><!-- 省略表单项... --><!-- 上传多张图片 --><el-upload v-model:file-list="pictureList" accept=".png,.jpg,.jpeg" :auto-upload="false"list-type="picture-card" :class="{ 'upload-hide': pictureList?.length === 10 }"  :on-change="handleChanges" :on-preview="handlePictureCardPreview"><el-icon><Plus /></el-icon></el-upload><el-dialog v-model="previewVisible"><img w-full :src="dialogImageUrl" alt="Preview Image" /></el-dialog><el-button type="primary" @click="handleSubmit">提交</el-button></el-form></el-dialog>
</template>
<script setup lang="ts">
import type { UploadProps, UploadFile, UploadFiles } from 'element-plus';
import _ from '@lodash';const visible = defineModel<boolean>({ default: false })
const props = defineProps<{type: 'add' | 'mod',id?: string
}>()
// 图片列表
const pictureList = ref<any[]>([])
// 图片预览显示
const previewVisible = ref(false)
// 图片预览url
const dialogImageUrl = ref('')
// 除图片外上传的其他参数
const ruleForm = reactive<Record<string, string>>({code: '',// 省略..
})// 编辑时数据回显
watch(() => visible.value, async (val) => {if (val && props.type === 'mod' && props.id) {await getEditData(props.id)}
}, {deep: true
})
// 上传图片
const handleChanges: UploadProps['onChange'] = (file: UploadFile, fileList: UploadFiles) => {// 文件格式const isPngOrJpg = ['image/png', 'image/jpeg'].includes(file.raw.type)if (!isPngOrJpg) {ElMessage.warning('上传文件格式错误!');return false;}// 文件名重复const isDuplicate = pictureList.value?.some(item => item.name === file.name);if (isDuplicate) {ElMessage.warning('该文件已存在,请重新选择!');// 移除新添加的重复文件fileList.pop();pictureList.value = fileList;} else {pictureList.value = fileList;}
};// 点击图片预览
const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile: UploadFile) => {dialogImageUrl.value = uploadFile.url!previewVisible.value = true
}
// 编辑时数据回显
async function getEditData(id?: number) {try {if (!id) return;await nextTick()const res = await getEditData({ id });if (res.code || _.isEmpty(res?.data)) throw new Error(res?.message);ruleForm.value = _.cloneDeep(res?.data);//表单项回显// 图片列表数据格式要以{url: '', name: ''}格式,才能正确回显pictureList.value = ruleForm.value.pictures?.map((item: any) => {return {url: item,name: item?.url?.split('/').pop()}})} catch (error) {if (error?.code === RESPONSE_CODE.CANCEL) return;ElMessage.error(error?.message);console.log(`[log] - getEditData - error:`, error);}
};
// 路径url转成file文件格式
async function convertUrlToFile(imageUrl: string, fileName: string) {try {// 发起GET请求获取资源,设置responseType为blobconst response = await fetch(imageUrl, { method: 'GET', mode: 'cors' });// 检查请求是否成功if (!response.ok) {throw new Error('图片加载失败!');}// 获取Blob数据const blob = await response.blob();// 创建File对象const file = new File([blob], fileName, { type: blob.type });return file;} catch (error) {console.error('图片url转换Blob失败!', error);return null;}
}
// 提交
async function handleSubmit() {try {// 表单校验省略...const fd = new FormData();// 除图片外的其他参数 (只上传图片,这步跳过)Object.keys(ruleForm).forEach(key => {fd.append(key, ruleForm[key]);});if (!_.isEmpty(pictureList.value)) {return ElMessage.warning('请先选择图片!');} else {const pictures = [] as File[]// 图片列表处理:for (let item of pictureList.value) {// 1. 图片url,需要先将url转换为文件格式,再上传if (!item?.raw) {const fileName = item?.url?.split('/').pop()const res = await convertUrlToFile(item.url, fileName)if (!res) returnpictures.push(res)} else {// 2. 图片文件,直接上传pictures.push(item?.raw)}}pictures.forEach((item) => {fd.append('pictures', item);});}const res = await updateData(fd);if (res?.code) throw new Error(res?.message);ElMessage.success(res?.message );visible.value = false;} catch (error) {console.log(`[log] - handleSubmit - error:`, error);ElMessage.error(error?.message );}
}
</script>
<style scoped>
:deep(.el-upload-list--picture-card) {--el-upload-list-picture-card-size: 94px;width: 100%;max-height: 210px;overflow: auto;
}:deep(.el-upload--picture-card) {--el-upload-picture-card-size: 94px
}.upload-hide {:deep(.el-upload--picture-card) {display: none;}
}
</style>

2. 踩坑记录

问题:在对图片列表遍历后处理时,一开始在forEach中进行文件格式转换操作,数据项无法插入formData中,但控制台打印有值。
原错误写法:

        if (!_.isEmpty(pictureList.value)) {const pictures = [] as File[]pictureList.value.forEach(async(item) => {if (!item?.raw) {const fileName = item?.url?.split('/').pop()const res = await convertUrlToFile(item.url, fileName)if(!res) returnpictures.push(res)} else {pictures.push(item?.raw)}})console.log(pictures,'pictures');// 这里能打印pictures.forEach((item) => {fd.append('pictures', item);});}

原因
forEach并发执行,在每次迭代时会立即执行指定的回调函数,并且不会等待上一次迭代的结果,所以并不能保证每次convertUrlToFile操作都已完成。

解决方法: 使用promise.all() 确保遍历执行的所有操作都完成后,再执行append操作。
另外,也可以使用for...of 循环,因为它是用迭代器实现的,每次迭代都会等待 next() 返回,所以可以保证执行的顺序。

if (!_.isEmpty(pictureList.value)) {const promises = pictureList.value.map(async (item) => {if (!item?.raw) {const fileName = item?.url?.split('/').pop();const res = await convertUrlToFile(item.url, fileName);if (!res) return;return res;} else {return item?.raw;}});Promise.all(promises).then((filledPictures) => {const pictures = filledPictures.filter(Boolean) as File[];pictures.forEach((item) => {fd.append('pictures', item);});}).catch((error) => {console.error('Error:', error);});
}

JavaScript 中的 BLOB 数据结构的使用介绍
谈谈JS二进制:File、Blob、FileReader、ArrayBuffer、Base64
Base64、Blob、File 三种类型的相互转换 最详细

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

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

相关文章

超频是什么意思?超频的好处和坏处

你是否曾经听说过超频&#xff1f;在电脑爱好者的圈子里&#xff0c;这个词似乎非常熟悉&#xff0c;但对很多普通用户来说&#xff0c;它可能还是一个神秘而陌生的存在。 电脑超频是什么意思 电脑超频&#xff08;Overclocking&#xff09;&#xff0c;顾名思义&#xff0c;是…

堆排序和Topk问题

堆排序 堆排序即利用堆的思想来进行排序&#xff0c; 总共分为两个步骤&#xff1a; 1. 建堆 升序&#xff1a;建大堆&#xff1b; 降序&#xff1a;建小堆 2 .利用堆删除思想来进行排序 利用堆删除思想来进行排序 建堆和堆删除中都用到了向下调整&#xff0c;因此掌握了…

外卖系统关于redis使用解决高并发情况

1、如何配置redis 在java中操作redis 操作步骤&#xff1a; 1、导入Spring Data Redis的maven坐标 2、配置Redis数据源 3、编写配置类&#xff0c;创建RedisTemplate对象 4、通过RedisTemplate对象操作Redis 2、Redis结合Lua脚本 减少网络开销&#xff1a;使用Lua脚本&#xf…

中医理疗元宇宙 科技赋能中医药产业走向国际市场

基于380亿参数量&#xff0c;对中医药海量文本进行数据训练&#xff0c;实现方剂优化、机制阐释和新适应症的精准发现……日前在天津召开的数智赋能大健康产业新质生产力暨第四届中医药国际发展大会上&#xff0c;由天士力医药集团与华为云共同开发的“数智本草”中医药大模型正…

37. 解数独 - 力扣(LeetCode)

基础知识要求&#xff1a; Java&#xff1a; 方法、for循环、if else语句、数组 Python&#xff1a; 方法、for循环、if else语句、列表 题目&#xff1a; 编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需 遵循如下规则&#xff1a; 数字 1-9 在每一行…

Windows搭建Nginx代理本地盘的文件(共享路径或本地路径)

文章目录 Windows搭建Nginx代理本地盘的文件 - 前言需求背景挂载网络共享路径检查连接状态下载Nginx编辑 Nginx 配置文件启动 Nginx检测Nginx是否成功启动使用方法远程共享路径示例本地文件示例 测试 Windows搭建Nginx代理本地盘的文件 - 前言 在开发过程中&#xff0c;确保文…

ChatGPT Mac客户端 下载安装教程(免费 不限次数使用 还支持语音聊天)

ChatGPT Mac客户端 下载安装教程&#xff08;免费 不限次数使用 还支持语音聊天&#xff09; 原文链接&#xff1a;https://blog.csdn.net/weixin_48311847/article/details/139248625 免费 不限次数使用 还支持语音聊天

曼城四连冠,剑南春与万千球迷共同见证“荣耀时刻”

执笔 | 洪大大 编辑 | 扬 灵 5月19日&#xff0c;英超2023-2024赛季第38轮比赛全面开打&#xff0c;凭借队员的出色发挥&#xff0c;曼城最终以3-1战胜西汉姆联&#xff0c;成功捧起了英超联赛的奖杯&#xff0c;成为英格兰足球顶级联赛100多年历史上第一支成就四连冠的豪门…

事务报错没有显示回滚导致DDL阻塞引发的问题

在业务开发过程中&#xff0c;显示的开启事务并且在事务处理过程中对不同的情况进行显示的COMMIT或ROLLBACK&#xff0c;这是一个完整数据库事务处理的闭环过程。 这种在应用开发逻辑层面去handle的事务执行的结果&#xff0c;既确保了事务操作的数据完整性&#xff0c;又遵循了…

探索未来设计新境界,PSAI插件 艺术创作神器来袭!

想象一下&#xff0c;如果有一个工具&#xff0c;能够让你的设计工作变得既简单又高效&#xff0c;那会是怎样的体验&#xff1f;现在&#xff0c;梦想成真了&#xff01; 这是一款革命性的PSAI设计插件&#xff0c;专为创意人士打造。它将彻底改变你的设计流程&#xff0c;让你…

【OpenCV】像素信息统计

介绍了计算像素均值、方差的API&#xff0c;以及统计像素信息的方法。相关API&#xff1a; minMaxLoc()mean()meanStdDev() 代码&#xff1a; #include "iostream" #include "opencv2/opencv.hpp"using namespace std; using namespace cv;int main(int…

【全开源】JAVA同城搬家系统源码小程序APP源码

JAVA同城搬家系统源码 特色功能&#xff1a; 强大的数据处理能力&#xff1a;JAVA提供了丰富的数据结构和算法&#xff0c;以及强大的并发处理能力&#xff0c;使得系统能够快速地处理大量的货物信息、司机信息、订单信息等&#xff0c;满足大规模物流的需求。智能路径规划&a…

香橙派 AIPro开发板上手测评

前言 最近拿到了一个新玩具&#xff1a;香橙派 AIPro。一个只比银行卡大一点点的开发板能带给我们多少惊喜呢&#xff1f;接下来就跟我一起来体验下这块开发板的魅力。 一、硬件配置 CPU&#xff1a;配备了4核64位ARM处理器&#xff0c;其中默认预留1个给AI处理器使用 NPU&am…

SpringBoot和Apache Doris实现实时广告推荐系统

本专题旨在向读者深度解读Apache Doris技术,探讨其与SpringBoot框架结合在各类实际应用场景中的角色与作用。本专题包括十篇文章,每篇文章都概述了一个特定应用领域,如大数据分析、实时报告系统、电商数据分析等,并通过对需求的解析、解决方案的设计、实际应用示例的展示以…

【Python实战】你还在冲会员看电影电视剧吗?Python带你实现各大资源免费看!

前言 halo&#xff0c;包子们下午好 今天给大家实现一个视频播放器&#xff0c;可以看任何电影&#xff0c;电视剧&#xff0c;不要再为以后看电视看电影而烦恼&#xff0c;今天是福利文章&#xff0c;相信我绝对有用&#xff01; 开发工具 Python版本&#xff1a;3.7.8 相…

驱动未来:IT行业的现状与发展趋势

前言 随着技术的不断进步&#xff0c;IT行业已成为推动全球经济和社会发展的关键力量。从云计算、大数据、人工智能到物联网、5G通信和区块链&#xff0c;这些技术正在重塑我们的生活和工作方式。本文将探讨IT行业的现状和未来发展趋势&#xff0c;并邀请行业领袖、技术专家和…

Follow Your Pose: Pose-Guided Text-to-Video Generation using Pose-Free Videos

清华深&港科&深先进&Tencent AAAI24https://github.com/mayuelala/FollowYourPose 问题引入 本文的任务是根据文本来生成高质量的角色视频&#xff0c;并且可以通过pose来控制任务的姿势&#xff1b;当前缺少video-pose caption数据集&#xff0c;所以提出一个两…

拿捏数据结构- 链式二叉树

链式二叉树的概念&#xff1a; 链式二叉树解决的是非完全二叉树解决不了的问题 什么意思呢&#xff0c;简单的说就是&#xff0c;链式二叉树 可以是下面三种二叉树 但是非链式二叉树只能是前两种 链式二叉树的存储 节点结构&#xff1a;首先定义一个结构体或类来表示二叉树的节…

单元测试的实现方式

单元测试的实现方式包括&#xff1a;人工静态检查、动态执行跟踪 人工静态检查 人工静态检查是一种单元测试实现方式&#xff0c;它主要依赖开发人员的人工代码审查和静态分析工具来识别潜在的代码问题。 代码审查&#xff1a;开发人员通过仔细检查代码来发现潜在的问题。他…

不怕YOLOv10高歌猛进,我有YOLOv8稳扎稳打

YOLOv10 出来有几天时间了&#xff0c;这次我没有选择第一时间出文章解析&#xff0c;如此频繁的发布数字版本的 YOLO 着实让人头疼&#xff0c;虽然数字的更新并非旧版技术的过时&#xff0c; 但是这肯定会让很多在校同学增加很多焦虑情绪。这里还是请大家辩证看待。 v10 这次…