在东莞做网站怎么做网站实惠
2026/3/10 11:42:03 网站建设 项目流程
在东莞做网站,怎么做网站实惠,南京网站设计哪家公司好,wordpress加印章插件1、前言 在现代Web应用中#xff0c;大文件上传是一个常见但充满挑战的需求。传统的一次性上传方式在面对大文件时存在诸多问题#xff1a;网络中断导致重新上传、上传超时、内存占用过高等。本文将详细介绍一套基于Vue3的企业级大文件分片上传解决方案#xff0c;该方案已…1、前言在现代Web应用中大文件上传是一个常见但充满挑战的需求。传统的一次性上传方式在面对大文件时存在诸多问题网络中断导致重新上传、上传超时、内存占用过高等。本文将详细介绍一套基于Vue3的企业级大文件分片上传解决方案该方案已在生产环境稳定运行具备以下核心能力分片上传将大文件切分为5MB的小块降低单次请求压力断点续传基于IndexedDB持久化上传进度刷新页面可继续上传并发控制可配置并发数平衡上传速度与服务器压力批量上传支持多文件同时上传统一管理上传状态任务管理实时追踪每个上传任务的状态和进度暂停/继续支持手动控制上传流程2、技术架构2.1 核心技术栈Vue 3 TypeScript // 前端框架 IndexedDB // 本地数据持久化 hash-wasm // MD5校验秒传判断 Element Plus // UI组件库 Axios // HTTP请求2.2 架构设计图┌─────────────┐ │ 用户上传 │ └──────┬──────┘ │ ▼ ┌─────────────────────────┐ │ 文件预处理层 │ │ - 文件切片(5MB) │ │ - MD5计算 │ │ - 去重检查 │ └──────┬──────────────────┘ │ ▼ ┌─────────────────────────┐ │ IndexedDB持久化层 │ │ - 分片信息存储 │ │ - 上传会话管理 │ │ - 断点续传支持 │ └──────┬──────────────────┘ │ ▼ ┌─────────────────────────┐ │ 并发上传控制层 │ │ - 并发队列管理 │ │ - 进度实时更新 │ │ - 错误重试机制 │ └──────┬──────────────────┘ │ ▼ ┌─────────────────────────┐ │ 服务端接口层 │ │ - 初始化上传 │ │ - 分片上传 │ │ - 合并分片 │ └─────────────────────────┘3、核心功能实现3.1 IndexedDB数据持久化使用IndexedDB存储分片信息是实现断点续传的关键。我设计了两个主要的对象存储// 分片信息结构 interface ChunkInfo { id: string; // 分片唯一标识 fileName: string; // 文件名 fileSize: number; // 文件大小 chunkIndex: number; // 分片索引 uploadId: string; // 上传ID isUploaded: boolean; // 是否已上传 eTag?: string; // 分片ETag用于合并 totalChunks: number; // 总分片数 firstChunkMd5?: string; // 首片MD5用于秒传 createdAt: number; // 创建时间 } // 上传会话结构 interface UploadSession { uploadId: string; // 上传ID fileName: string; // 文件名 fileSize: number; // 文件大小 totalChunks: number; // 总分片数 chunksUploaded: number; // 已上传分片数 firstChunkMd5: string; // 首片MD5 isCompleted: boolean; // 是否完成 createdAt: number; // 创建时间 lastUpdated: number; // 最后更新时间 }亮点使用fileName fileSize firstChunkMd5三元组作为文件唯一标识实现秒传功能通过uploadId索引快速检索分片信息记录lastUpdated时间戳支持清理过期上传任务3.2 智能断点续传断点续传的实现逻辑// 1. 查找已存在的上传会话 const existingSession await findExistingUpload( file.name, fileSize, firstChunkMd5 ); if (existingSession) { // 2. 恢复上传会话 uploadId existingSession.uploadId; uploadedChunks await getChunksByUploadId(uploadId); console.log(继续上传 ${file.name}); } else { // 3. 创建新上传会话 const resp await multipartUpload({ ossStatus: initiate, originalName: file.name, md5Digest: firstChunkMd5 }); uploadId resp.data.uploadId; }实现要点首次上传时计算首片MD5后续上传通过MD5匹配历史会话已上传的分片直接跳过只上传未完成的分片支持跨浏览器会话的断点续传3.3 并发控制策略采用批量并发上传策略既保证上传速度又避免过载服务器// 并发上传核心逻辑 const uploadChunksConcurrently async ( chunks: Blob[], uploadId: string, // ... 其他参数 options: UploadOptions {} ) { const { concurrency 3 } options; // 默认并发数为3 const pendingChunks []; // 待上传分片索引 // 1. 过滤已上传的分片 for (let i 0; i totalChunks; i) { if (!partUploadList[i]) { pendingChunks.push(i); } } // 2. 分批并发上传 for (let i 0; i pendingChunks.length; i concurrency) { const batch pendingChunks.slice(i, i concurrency); // 3. Promise.all 并发执行 const batchPromises batch.map(chunkIndex uploadChunk(chunks[chunkIndex], chunkIndex, ...) ); const results await Promise.all(batchPromises); // 4. 更新进度 await updateProgress(uploadId, completed, totalChunks); } };性能优化默认并发数为3可根据网络环境动态调整使用Promise.all确保一批分片全部完成后再进行下一批实时更新进度条提升用户体验3.4 批量文件上传支持多文件同时上传每个文件独立管理const handleBatchFileUpload async ( files: File[], options: UploadOptions {} ) { const uploadPromises files.map(async (file) { try { // 每个文件独立上传 const result await handleFileUpload(file, { ...options, onProgressUpdate: (progress) { // 更新单文件进度 batchUploadResults.value.set(file.name, { progress, status: uploading }); // 计算总体进度 const totalProgress calculateTotalProgress(); options.onProgressUpdate?.(totalProgress); } }); return { success: true, file, result }; } catch (error) { return { success: false, file, error }; } }); // 等待所有文件上传完成 const results await Promise.all(uploadPromises); return results; };功能特性每个文件使用fileName fileSize作为唯一标识支持部分文件失败时的错误处理提供统一的批量进度管理3.5 文件去重与状态管理使用Map结构实现文件级别的状态管理防止重复上传// 文件级别状态管理 const fileUploadStates refMapstring, boolean(new Map()); const isFileUploading (fileKey: string) { return fileUploadStates.value.get(fileKey) true; }; const handleFileUpload async (file: File) { const fileKey ${file.name}_${file.size}; // 防止重复上传 if (isFileUploading(fileKey)) { throw new Error(文件已在上传中); } setFileUploadState(fileKey, true); try { // 执行上传... } finally { // 清除状态 setFileUploadState(fileKey, false); } };4、上传任务管理4.1 任务状态追踪实现了完整的任务生命周期管理interface UploadTaskStatus { uploadId: string; fileName: string; progress: number; status: uploading | paused | completed | error; file: File; } // 任务状态存储 const uploadTasks refMapstring, UploadTaskStatus(new Map()); // 添加任务 const addUploadTask (uploadId: string, file: File) { uploadTasks.value.set(uploadId, { uploadId, fileName: file.name, progress: 0, status: uploading, file }); }; // 更新进度 const updateTaskProgress (uploadId: string, progress: number) { const task uploadTasks.value.get(uploadId); if (task) { task.progress progress; uploadTasks.value.set(uploadId, { ...task }); } };4.2 任务清理机制灵活的任务清理功能// 清除指定任务 const clearUploadTask async (uploadId: string) { const db await initDB(); const transaction db.transaction( [upload_sessions, upload_chunks], readwrite ); // 删除上传会话 transaction.objectStore(upload_sessions).delete(uploadId); // 删除相关分片 const chunkStore transaction.objectStore(upload_chunks); const index chunkStore.index(uploadId); const request index.openKeyCursor(IDBKeyRange.only(uploadId)); request.onsuccess (event) { const cursor event.target.result; if (cursor) { chunkStore.delete(cursor.primaryKey); cursor.continue(); } }; }; // 清除所有任务 const clearAllUploadData async () { const db await initDB(); const transaction db.transaction( [upload_sessions, upload_chunks], readwrite ); transaction.objectStore(upload_sessions).clear(); transaction.objectStore(upload_chunks).clear(); };5、使用示例5.1 单文件上传template div classupload-container input typefile changehandleFileChange acceptvideo/* / el-progress :percentagepercent :statusuploadStatus / div classactions el-button clickpauseCurrentUpload暂停/el-button el-button clickcontinueCurrentUpload继续/el-button /div /div /template script setup langts import { useFileUpload } from /composables/useFileUpload; const { percent, uploadStatus, handleFileUpload, pauseCurrentUpload, continueCurrentUpload } useFileUpload(); const handleFileChange async (event: Event) { const file (event.target as HTMLInputElement).files?.[0]; if (!file) return; try { const result await handleFileUpload(file, { concurrency: 5, // 并发数 onProgressUpdate: (progress) { console.log(上传进度: ${progress}%); }, onChunkComplete: (index, total) { console.log(分片 ${index 1}/${total} 完成); } }); console.log(上传成功:, result); } catch (error) { console.error(上传失败:, error); } }; /script5.2 批量上传template div classbatch-upload input typefile multiple changehandleBatchChange / div classfile-list div v-foritem in batchProgress :keyitem.fileName classfile-item span{{ item.fileName }}/span el-progress :percentageitem.progress / span{{ item.status }}/span /div /div /div /template script setup langts import { ref } from vue; import { useFileUpload } from /composables/useFileUpload; const { handleBatchFileUpload, getBatchUploadProgress } useFileUpload(); const batchProgress ref([]); const handleBatchChange async (event: Event) { const files Array.from( (event.target as HTMLInputElement).files || [] ); const { successResults, failedResults } await handleBatchFileUpload( files, { concurrency: 3, onProgressUpdate: (progress) { batchProgress.value getBatchUploadProgress(); } } ); console.log(成功: ${successResults.length}); console.log(失败: ${failedResults.length}); }; /script5.3 断点续传管理template div classresume-uploads h3未完成的上传/h3 div v-forsession in incompleteUploads :keysession.uploadId span{{ session.fileName }}/span span{{ session.chunksUploaded }}/{{ session.totalChunks }}/span el-button clickresumeUpload(session)继续/el-button el-button clickclearTask(session.uploadId)删除/el-button /div el-button clickclearAll清除所有/el-button /div /template script setup langts import { ref, onMounted } from vue; import { useFileUpload } from /composables/useFileUpload; const { getIncompleteUploads, clearUploadTask, clearAllUploadData } useFileUpload(); const incompleteUploads ref([]); onMounted(async () { incompleteUploads.value await getIncompleteUploads(); }); const resumeUpload async (session) { // 实现继续上传逻辑 }; const clearTask async (uploadId: string) { await clearUploadTask(uploadId); incompleteUploads.value await getIncompleteUploads(); }; const clearAll async () { await clearAllUploadData(); incompleteUploads.value []; }; /script6、性能优化建议6.1 分片大小选择// 根据文件大小动态调整分片大小 const getOptimalChunkSize (fileSize: number) { if (fileSize 50 * 1024 * 1024) { return 2 * 1024 * 1024; // 小于50MB使用2MB } else if (fileSize 500 * 1024 * 1024) { return 5 * 1024 * 1024; // 小于500MB使用5MB } else { return 10 * 1024 * 1024; // 大于500MB使用10MB } };6.2 并发数动态调整// 根据网络状况调整并发数 const adjustConcurrency (networkSpeed: number) { if (networkSpeed 10) { return 6; // 高速网络 } else if (networkSpeed 5) { return 4; // 中速网络 } else { return 2; // 低速网络 } };6.3 内存优化// 使用流式读取避免一次性加载整个文件 const readChunkAsStream async (file: File, start: number, end: number) { const chunk file.slice(start, end); return chunk; // 返回Blob按需读取 };6.4 IndexedDB清理策略// 定期清理过期上传任务7天 const cleanExpiredSessions async () { const sessions await getIncompleteUploads(); const now Date.now(); const SEVEN_DAYS 7 * 24 * 60 * 60 * 1000; for (const session of sessions) { if (now - session.lastUpdated SEVEN_DAYS) { await clearUploadTask(session.uploadId); } } };7、注意事项与最佳实践7.1 错误处理// 完善的错误处理机制 try { await handleFileUpload(file); } catch (error) { if (error.uploadId) { // 清理失败的上传任务 await clearUploadTask(error.uploadId); } // 根据错误类型提示用户 if (error.message.includes(网络)) { ElMessage.error(网络错误请检查网络连接); } else if (error.message.includes(空间)) { ElMessage.error(存储空间不足); } else { ElMessage.error(上传失败请重试); } }7.2 安全性考虑// 文件类型校验 const validateFile (file: File) { const allowedTypes [video/mp4, video/avi, video/mov]; if (!allowedTypes.includes(file.type)) { throw new Error(不支持的文件类型); } // 文件大小限制5GB const MAX_SIZE 5 * 1024 * 1024 * 1024; if (file.size MAX_SIZE) { throw new Error(文件大小超过限制); } };7.3 用户体验优化// 显示友好的进度提示 const getProgressText (progress: number, fileName: string) { if (progress 0) { return 准备上传 ${fileName}...; } else if (progress 100) { return 正在上传 ${fileName} (${progress}%); } else { return ${fileName} 上传完成; } }; // 剩余时间估算 const estimateRemainingTime ( uploadedBytes: number, totalBytes: number, startTime: number ) { const elapsed Date.now() - startTime; const speed uploadedBytes / elapsed; const remaining (totalBytes - uploadedBytes) / speed; return Math.ceil(remaining / 1000); // 返回秒数 };8、监控与日志8.1 上传统计// 收集上传统计数据 interface UploadStats { totalFiles: number; successCount: number; failureCount: number; totalBytes: number; averageSpeed: number; averageTime: number; } const collectStats (results: any[]) { return { totalFiles: results.length, successCount: results.filter(r r.success).length, failureCount: results.filter(r !r.success).length, totalBytes: results.reduce((sum, r) sum r.file.size, 0), // ... 其他统计 }; };8.2 日志记录// 结构化日志 const logUploadEvent (event: string, data: any) { console.log([Upload ${event}], { timestamp: new Date().toISOString(), ...data }); }; // 使用示例 logUploadEvent(START, { fileName, fileSize, uploadId }); logUploadEvent(CHUNK_COMPLETE, { chunkIndex, totalChunks }); logUploadEvent(SUCCESS, { fileName, duration });9、总结本文介绍的大文件分片上传方案具有以下优势高可靠性基于IndexedDB的断点续传确保上传不会因刷新而中断高性能并发控制分片上传充分利用网络带宽易扩展模块化设计方便集成到现有项目用户友好实时进度反馈支持暂停/继续操作企业级完善的错误处理、日志记录和监控机制该方案已在生产环境验证可处理GB级别的大文件上传适用于视频上传、大文件传输等场景。10、参考资源IndexedDB API 文档Vue3 Composition APIAxios 官方文档Element Plus 组件库11、后续优化方向WebWorker 优化将MD5计算移至Worker线程避免阻塞主线程预签名URL使用预签名URL直传OSS减少服务器压力智能重试指数退避算法实现失败分片的智能重试压缩上传在上传前对文件进行压缩节省带宽秒传功能完整文件MD5校验实现真正的秒传

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询