多張圖片上傳、圖片回顯、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.pswp.cn/bicheng/17730.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/17730.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/17730.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

超頻是什么意思?超頻的好處和壞處

你是否曾經聽說過超頻&#xff1f;在電腦愛好者的圈子里&#xff0c;這個詞似乎非常熟悉&#xff0c;但對很多普通用戶來說&#xff0c;它可能還是一個神秘而陌生的存在。 電腦超頻是什么意思 電腦超頻&#xff08;Overclocking&#xff09;&#xff0c;顧名思義&#xff0c;是…

PCIe (1)

計算PCIe的吞吐 PCIe吞吐依賴以下因素 >protocol overhead >payload size >completion latency >flow control update latency >characteristics of the devices that form the link Protocol Overhead 如果是8B/10B的編碼,那么需要25%的開銷。 對于Gen…

前端面試題大合集7----模塊化/工程化/ES6+標準

一、簡述webpack的核心原理 &#xff08;1&#xff09;一切皆模塊 正如JS文件可以是一個“模塊”一樣&#xff0c;其它的文件也可視作模塊。因此可以執行require(myJsFile.js)&#xff0c;亦可執行require(myCssFile.css)&#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…

FolkMQ v1.5.1 發布(“新式”國產消息中間件)

FolkMQ 是個“新式”的消息中間件。強調&#xff1a;“小而巧”、“簡而強”。 功能簡表 角色功能生產者&#xff08;客戶端&#xff09;發布普通消息、Qos0消息、定時消息、順序消息、可過期消息、事務消息、廣播消息消費者&#xff08;客戶端&#xff09;訂閱、取消訂閱。消…

前端面試題日常練-day27 【面試題】

題目 希望這些選擇題能夠幫助您進行前端面試的準備&#xff0c;答案在文末。 1. 在Vue中&#xff0c;以下哪個選項可以用于監聽數據的變化并執行相應的操作&#xff1f; a) computed b) methods c) data d) watch 2. 在Vue中&#xff0c;以下哪種方式可以實現組件之間的通信…

中醫理療元宇宙 科技賦能中醫藥產業走向國際市場

基于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 免費 不限次數使用 還支持語音聊天

mysql 排序、查詢執行流程、幻讀

文章目錄 MySQL的 ORDER BY 執行流程示例表和查詢語句執行流程全字段排序Rowid 排序全字段排序 VS rowid排序聯合索引優化覆蓋索引優化 小結思考題問題執行過程中是否需要排序&#xff1f;如何在數據庫端實現不排序&#xff1f;實現分頁需求 使用ORDER BY RAND()內存臨時表與磁…

ANDROID OLLVM 混淆配置

安裝環境 MacOSGITCMAKENDK - 21.1.6352462 步驟 1. 編譯項目 此項目版本較低 https://github.com/obfuscator-llvm/obfuscator &#xff0c;我們使用 https://github.com/heroims/obfuscator 進行編譯 git clone https://github.com/heroims/obfuscator.gitcd obfuscator…

曼城四連冠,劍南春與萬千球迷共同見證“榮耀時刻”

執筆 | 洪大大 編輯 | 揚 靈 5月19日&#xff0c;英超2023-2024賽季第38輪比賽全面開打&#xff0c;憑借隊員的出色發揮&#xff0c;曼城最終以3-1戰勝西漢姆聯&#xff0c;成功捧起了英超聯賽的獎杯&#xff0c;成為英格蘭足球頂級聯賽100多年歷史上第一支成就四連冠的豪門…

事務報錯沒有顯示回滾導致DDL阻塞引發的問題

在業務開發過程中&#xff0c;顯示的開啟事務并且在事務處理過程中對不同的情況進行顯示的COMMIT或ROLLBACK&#xff0c;這是一個完整數據庫事務處理的閉環過程。 這種在應用開發邏輯層面去handle的事務執行的結果&#xff0c;既確保了事務操作的數據完整性&#xff0c;又遵循了…

簡單句語法

簡單句是指包含一個主語和一個謂語的句子&#xff0c;它表達一個完整的思想。簡單句是構成更復雜句子的基礎。 簡單句的兩種基本結構 簡單句可以分為兩種基本結構&#xff1a; 主謂結構: 描述主語所做的動作或行為&#xff0c;也就是 “做什么”。 主系結構: 描述主語的狀態…

Python2和Python3對utf8的實現方式有什么區別?

# -*- coding: utf8 -*- 是一個特殊的文件頭部注釋&#xff0c;通常出現在Python 2的源代碼文件的開頭。這個注釋告訴Python解釋器&#xff0c;該源文件使用的是UTF-8編碼。這對于包含非ASCII字符&#xff08;例如中文字符、特殊符號等&#xff09;的Python源代碼文件來說非常重…

探索未來設計新境界,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…

談談如何建立可落地的數字化轉型戰略

數字化轉型戰略是指將數字技術集成到企業或組織的所有領域&#xff0c;從根本上改變其運營方式以及為客戶提供價值的方式。它涉及采用新技術并重新思考現有業務流程&#xff0c;以提高效率、生產力和客戶滿意度。 成功的數字化轉型戰略需要采用涉及人員、流程和技術的整體方法。…