注如果想直接使用关注私信要websocet上传地址就可以直接复制到自己的项目使用图片上传了上传成功后会返回一个图片地址可以根据自身要求去使用。1.组件完整代码template div classUploadImg !-- 图片放置位置 -- TransitionGroup namelist div classimgList keyimgList img :srcitem alt v-for(item, index) in fileList :keyindex / !-- 上传中图片 -- div classloading v-loadingloading v-ifloading keyloading /div /div /TransitionGroup div classaddBtn clickchooseImg el-icon size40Plus //el-icon /div !-- 上传文件最重要的东西 acceptimage/*控制只能选择图片-- input v-showfalse typefile reffileInput :multiplemultiple acceptimage/* changechangeImgSuccess / /div /template script setup import { ref, onMounted, watch, defineProps, defineEmits, onBeforeUnmount, } from vue; import { ElNotification } from element-plus; import { ElMessage, ElMessageBox } from element-plus; const props defineProps({ multiple: { type: Boolean, default: false, }, fileListProps: { type: Array, default: () [], }, max: { type: Number, default: 1, }, }); const fileList ref([]); // 图片加载状态 const loading ref(false); // 监听弹窗关闭清空图片 watch( () props.fileListProps, (newVal) { console.log(111); newVal.length 0 ? (fileList.value []) : (fileList.value newVal); loading.value false; }, { immediate: true, } ); const emits defineEmits([update:fileListProps]); // 导入chunk import { splitBase64IntoChunks } from /utils/splitBase64IntoChunks.js; const fileInput ref(null); // 打开图片选择 const chooseImg () { if (loading.value) { ElNotification({ title: Error, message: 图片正在上传中请稍后重试, type: error, }); return; } if (fileList.value.length props.max) { ElMessageBox.confirm( 图片已是最大限制如果强制选择图片前面选中的第一张图片将会被以出确定要这么做吗?, 提示, { confirmButtonText: 确定, cancelButtonText: 取消, type: warning, } ) .then(() { fileInput.value.click(); // 删掉数据第一个数据 fileList.value.splice(0, 1); }) .catch(() { ElMessage({ type: info, message: 您取消了, }); }); return; } fileInput.value.click(); }; // 选择完图片后 const changeImgSuccess (event) { console.log(event.target.files); if (event.target.files.length 0) { ElNotification({ title: Info, message: 你取消了选择图片, type: info, }); return; } if (props.multiple) { // 多选 console.log(多选图片); const files event.target.files; for (let i 0; i files.length; i) { const file files[i]; //文件名 const fileName new Date().getTime() file.name; const reader new FileReader(); reader.addEventListener(load, () { const base64Data reader.result; // 在连接建立后可以发送消息或执行其他操作 socket.value.send( JSON.stringify({ event: imgName, data: fileName, }) ); // 分块 let chunks splitBase64IntoChunks(base64Data, 1024 * 64); console.log(chunks, chunks); // 逐个发送块数据 chunks.forEach((chunk, index) { let eventData { event: base64Data, // 事件标识字段 data: chunk, }; setTimeout(() { socket.value.send({ data: JSON.stringify(eventData), success: () { console.log(块 ${index 1}/${chunks.length} 已发送); if (index chunks.length - 1) { // 所有块数据已发送发送结束消息 socket.value.send({ data: JSON.stringify({ data: {}, event: upload_complete, }), success: () { console.log(上传完成); // 关闭定时器或执行其他操作 }, }); } }, }); }, index * 200); // 延迟发送以避免过快发送 }); }); reader.readAsDataURL(file); } } else { // 单选 console.log(单选图片); const file event.target.files[0]; loading.value true; //文件名 const fileName new Date().getTime() file.name; const reader new FileReader(); reader.addEventListener(load, () { let base64Data reader.result; const type file.type; // 这里的base64 有data:image/jpeg;base64, 不需要把它截取掉 const prefix data:${type};base64,; if (base64Data.startsWith(prefix)) { base64Data base64Data.substr(prefix.length); } console.log(base64Data, base64Data); // 在连接建立后可以发送消息或执行其他操作 socket.value.send( JSON.stringify({ event: imgName, data: fileName, }) ); // 分块 let chunks splitBase64IntoChunks(base64Data, 1024 * 64); console.log(chunks, chunks); // 逐个发送块数据 chunks.forEach((chunk, index) { let eventData { event: base64Data, // 事件标识字段 data: chunk, }; setTimeout(() { console.log(发送了, index); socket.value.send(JSON.stringify(eventData)); if (index chunks.length - 1) { console.log(最后一次了); socket.value.send( JSON.stringify({ data: {}, event: upload_complete, }) ); } }, index * 200); // 延迟发送以避免过快发送 }); }); reader.readAsDataURL(file); } }; // 初始化websocket const socket ref(null); const initWebsocket () { if (!window.WebSocket) { alert(您的浏览器不支持WebSocket); } else { // 获取ip地址或者域名 const ip location.hostname; const wsIP ws:// ip :8080; socket.value new WebSocket(wsIP); // 替换为你的 WebSocket 服务器地址 // socket.value new WebSocket(ws://127.0.0.1:8080); // 替换为你的 WebSocket 服务器地址 socket.value.onopen () { console.log(WebSocket 连接已建立); // 开启心跳 heartBeat(); }; socket.value.onmessage (event) { isReconnect.value true; heartRestartTimer.value clearTimeout(heartRestartTimer.value); console.log(收到消息:, event.data); const jsonData JSON.parse(event.data); // 处理收到的消息数据 if (jsonData.event sentImgUrl) { console.log(结束图片上传, jsonData); const uploadUrl https:// jsonData.data.imgUrl; fileList.value.push(uploadUrl); emits(update:fileListProps, fileList.value); loading.value false; console.log(收到的图片url, uploadUrl, fileList.value); } }; socket.value.onclose () { console.log(WebSocket 连接已关闭); isReconnect.value false; //连接都关闭了肯定要重新连接 reconnect(); // 在连接关闭后可以执行清理操作或重新连接等处理 }; } }; // 存放心跳定时器 const heartBeatTimer ref(null); // 存放错误心跳定时器 const heartBeatErrorTimer ref(null); // 心跳重新连接 const heartRestartTimer ref(null); // 开启心跳 const heartBeat () { // 每 60 秒发送一次心跳 const heartBeatTime 60 * 1000; // 心跳包 const heartBeatData { event: heartBeat, data: {}, }; heartBeatTimer.value clearInterval(heartBeatTimer.value); heartRestartTimer.value clearTimeout(heartRestartTimer.value); heartBeatTimer.value setInterval(() { socket.value.send(JSON.stringify(heartBeatData)); // 如果20s收不到消息要么断开要么后端出错 heartRestartTimer.value setTimeout(() { isReconnect.value false; reconnect(); }, 20 * 1000); }, heartBeatTime); }; // 是否需要重连的开关 true 表示不需要重连 const isReconnect ref(false); // 重新连接 const reconnect () { if (isReconnect.value) { return; } isReconnect.value true; // 关闭当前连接 socket.value.close(); // 等待1s后重新连接 heartBeatErrorTimer.value setTimeout(() { initWebsocket(); }, 1000); }; onMounted(() { initWebsocket(); }); onBeforeUnmount(() { // 关闭当前连接 socket.value.close(); }); /script style scoped langscss .list-enter-active, .list-leave-active { transition: opacity 0.5s ease; } .list-enter-from, .list-leave-to { opacity: 0; } .list-leave-active { position: absolute; } .UploadImg { display: flex; .imgList { display: flex; position: relative; .loading { width: 100px; height: 100px; border-radius: 10px; border: 1px solid #d9d9d9; } img { margin: 0 5px; width: 100px; height: 100px; border-radius: 10px; border: 1px solid #d9d9d9; } } .addBtn { width: 100px; height: 100px; border-radius: 10px; // 边框虚线 border: 1px dashed #d9d9d9; background-color: #fafafa; display: flex; align-items: center; justify-content: center; cursor: pointer; margin-left: 5px; } } /style图片分块上传的代码// 分割大的base64数据为块 export function splitBase64IntoChunks(base64Data, chunkSize) { const totalChunks Math.ceil(base64Data.length / chunkSize); const chunks []; for (let i 0; i totalChunks; i) { const start i * chunkSize; const end start chunkSize; const chunk base64Data.slice(start, end); chunks.push(chunk); } return chunks; }2.组件使用代码//使用 el-form-item label图片 UploadImg v-model:fileListPropsruleForm.photoList / /el-form-item //引入 import UploadImg from /components/base/UploadImg.vue; 绑上ruleForm.photoList》》图片上传成功后的地址存放位置。必须是数组3.使用效果如下图所示