2026/2/21 9:55:53
网站建设
项目流程
网站建设合同书相关附件,成都网站开发 Vr,网站建设开封软件制作,2015年做那些网站致富开发者日记#xff1a;2023年11月25日 周六 阴 项目名称#xff1a;跨平台大文件传输系统#xff08;WebUploaderVue3SpringBoot腾讯云COS#xff09; 项目背景与核心挑战
作为河南独立开发者#xff0c;近期承接了一个高复杂度外包项目#xff0c;客户要求实现20G级文件…开发者日记2023年11月25日 周六 阴项目名称跨平台大文件传输系统WebUploaderVue3SpringBoot腾讯云COS项目背景与核心挑战作为河南独立开发者近期承接了一个高复杂度外包项目客户要求实现20G级文件/文件夹跨浏览器上传下载技术栈涉及前端Vue3TypeScript WebUploader兼容IE8后端SpringBoot 3.0 Oracle 21c存储腾讯云COS需处理分片上传与合并兼容性从IE8到现代浏览器Chrome/Firefox/Safari/Edge/Opera现存痛点网上开源方案仅支持单文件上传无完整文件夹层级解析逻辑IE8的ActiveX控件在Windows 11上频繁崩溃Oracle数据库与MySQL的序列化差异导致进度存储失败20G文件传输时SpringBoot默认超时时间1分钟严重不足技术方案设计前端架构Vue3 WebUploaderChrome/FirefoxIE8用户选择文件/文件夹浏览器类型使用FileSystemDirectoryHandle API调用ActiveX控件递归读取生成文件树结构JSON计算文件ETag替代MD5兼容IE8分片上传WebUploader本地存储进度IndexedDBlocalStorage后端架构SpringBoot Oracle是否是否接收分片是否首片创建Oracle任务记录序列BLOB更新分片进度JDBC批处理存储分片到临时目录是否全部分片完成合并文件并上传COS返回继续上传指令关键数据库设计Oracle-- 上传任务表CREATETABLEUPLOAD_TASK(TASK_ID VARCHAR2(36)PRIMARYKEY,FILE_ETAG VARCHAR2(64)NOTNULL,RELATIVE_PATH VARCHAR2(1024),-- 保留文件夹层级如 /project/src/TOTAL_CHUNKS NUMBER,UPLOADED_CHUNKS NUMBERDEFAULT0,STATUSVARCHAR2(20)CHECK(STATUSIN(PENDING,UPLOADING,COMPLETED,FAILED)),COS_KEY VARCHAR2(1024),CREATE_TIMETIMESTAMPDEFAULTSYSTIMESTAMP);-- 分片存储表Oracle BLOB优化CREATETABLEUPLOAD_CHUNK(CHUNK_ID VARCHAR2(72)PRIMARYKEY,-- TASK_IDCHUNK_INDEXTASK_ID VARCHAR2(36)REFERENCESUPLOAD_TASK(TASK_ID),CHUNK_INDEX NUMBER,CHUNK_DATABLOB,CONSTRAINTUK_TASK_CHUNKUNIQUE(TASK_ID,CHUNK_INDEX));核心代码实现前端文件夹上传与断点续传Vue3 TypeScript// src/components/FolderUploader.vueimport{ref,onMounted}fromvue;importWebUploaderfromwebuploader;import{calculateFileETag}from/utils/fileHash;// 自定义ETag计算兼容IE8exportdefault{setup(){consttaskListref([]);constuploaderref(null);// 初始化上传器兼容IE8constinitUploader(){constisIE8document.documentMode8;uploader.valueWebUploader.create({swf:/static/Uploader.swf,// IE8回退server:/api/upload/chunk,chunked:true,chunkSize:isIE8?4*1024*1024:20*1024*1024,// IE8限制4MB分片threads:isIE8?1:5,formData:{taskId:localStorage.getItem(currentTaskId)||},timeout:0// 禁用超时由后端控制});// 恢复未完成任务从IndexedDBrestoreTasksFromDB();};// 递归解析文件夹跨浏览器consthandleFolderSelectasync(e:Event){constinpute.targetasHTMLInputElement;constfilesinput.files;if(!files?.length)return;constparseFolderasync(entries:FileSystemEntry[],parentPath){for(letentryofentries){if(entry.isFile){constfile(entryasFileSystemFileEntry).file!;constrelativePathparentPath?${parentPath}/${entry.name}:entry.name;awaitaddUploadTask(file,relativePath);}elseif(entry.isDirectory){constdirReader(entryasFileSystemDirectoryEntry).createReader();constnewEntriesawaitnewPromise(resolve{dirReader.readEntries(resolve);});awaitparseFolder(newEntries,parentPath?${parentPath}/${entry.name}:entry.name);}}};// Chrome/Firefox使用showDirectoryPickerif(files[0].webkitGetAsEntry){constentryfiles[0].webkitGetAsEntry();if(entry?.isDirectory){constdirReaderentry.createReader();constentriesawaitnewPromise(resolve{dirReader.readEntries(resolve);});awaitparseFolder(entries);}else{awaitaddUploadTask(files[0],files[0].name);}}// IE8使用ActiveX需用户授权elseif(window.ActiveXObject){// ActiveX实现代码省略需处理权限弹窗}};// 添加上传任务constaddUploadTaskasync(file:File,relativePath:string){constetagawaitcalculateFileETag(file);// 使用CRC32文件大小替代MD5consttask{file,relativePath,etag,chunkCount:Math.ceil(file.size/uploader.value!.options.chunkSize),uploadedChunks:0};// 检查本地是否有未完成记录constexistingTaskawaitgetTaskFromDB(etag);if(existingTask){task.uploadedChunksexistingTask.uploadedChunks;}taskList.value.push(task);saveTaskToDB(task);startUpload(task);};onMounted((){initUploader();document.getElementById(folderInput)?.addEventListener(change,handleFolderSelect);});return{taskList,uploader};}};后端SpringBoot分片处理与COS上传// src/main/java/com/example/uploader/controller/UploadController.javaRestControllerRequestMapping(/api/upload)publicclassUploadController{AutowiredprivateUploadTaskRepositorytaskRepository;AutowiredprivateUploadChunkRepositorychunkRepository;AutowiredprivateCOSClientcosClient;// 腾讯云COS客户端// 分片上传接口PostMapping(/chunk)publicResponseEntityhandleChunk(RequestParam(file)MultipartFilefile,RequestParam(taskId)StringtaskId,RequestParam(chunkIndex)intchunkIndex,RequestParam(totalChunks)inttotalChunks,RequestParam(etag)Stringetag,RequestParam(relativePath)StringrelativePath){// 1. 查询或创建任务UploadTasktasktaskRepository.findById(taskId).orElseGet(()-{UploadTasknewTasknewUploadTask();newTask.setTaskId(taskId);newTask.setFileEtag(etag);newTask.setRelativePath(relativePath);newTask.setTotalChunks(totalChunks);newTask.setStatus(UPLOADING);returntaskRepository.save(newTask);});// 2. 保存分片到Oracle BLOBUploadChunkchunknewUploadChunk();chunk.setChunkId(taskId_chunkIndex);chunk.setTaskId(taskId);chunk.setChunkIndex(chunkIndex);try{chunk.setChunkData(file.getBytes());chunkRepository.save(chunk);}catch(IOExceptione){returnResponseEntity.status(500).body(分片保存失败);}// 3. 更新任务进度task.setUploadedChunks(chunkIndex1);taskRepository.save(task);// 4. 检查是否全部上传完成if(task.getUploadedChunks()task.getTotalChunks()){// 启动异步合并任务mergeAndUploadToCOS(task);returnResponseEntity.ok({\status\:\COMPLETED\});}returnResponseEntity.ok({\status\:\SUCCESS\});}// 异步合并并上传COSAsyncpublicvoidmergeAndUploadToCOS(UploadTasktask){try{// 1. 从Oracle读取所有分片ListchunkschunkRepository.findByTaskIdOrderByChunkIndex(task.getTaskId());// 2. 合并文件流式处理避免内存溢出PathtempFileFiles.createTempFile(upload_,.tmp);try(OutputStreamoutFiles.newOutputStream(tempFile)){for(UploadChunkchunk:chunks){out.write(chunk.getChunkData());}}// 3. 上传到腾讯云COSStringcosKeyuploads/task.getTaskId()/task.getRelativePath();cosClient.putObject(newPutObjectRequest(your-bucket,cosKey,tempFile.toFile()));// 4. 更新任务状态task.setStatus(COMPLETED);task.setCosKey(cosKey);taskRepository.save(task);// 5. 清理临时文件和分片Files.deleteIfExists(tempFile);chunkRepository.deleteByTaskId(task.getTaskId());}catch(Exceptione){task.setStatus(FAILED);taskRepository.save(task);}}}关键问题解决IE8兼容性使用标签替代已废弃的加载Uploader.swf通过document.execCommand(SaveAs)实现IE8的下载功能Oracle性能优化对UPLOAD_CHUNK表的TASK_ID字段建立索引使用JDBC批处理更新进度addBatch()executeBatch()大文件传输超时在SpringBoot配置中增加server:tomcat:connection-timeout:0# 禁用连接超时spring:servlet:multipart:max-file-size:20GBmax-request-size:20GB文件夹层级保留前端传递relativePath参数如/docs/2023/report.pdf后端直接存储该路径上传COS时保持原样求助与社区支持目前遇到以下难题已在QQ群374992201发布详细日志IE8的ActiveX控件在Windows 11上频繁弹出权限警告Oracle BLOB存储20G文件时出现ORA-01653表空间不足错误SpringBoot异步任务在合并大文件时被K8s容器终止完整代码仓库前端https://gitee.com/yourname/vue3-folder-uploader后端https://gitee.com/yourname/springboot-cos-uploader明日计划实现WebUploader的IE8进度条显示编写Oracle分片表的分区策略按任务ID哈希分区测试20G文件在低带宽2Mbps下的传输稳定性日记结束附技术栈对比表模块原方案当前方案优化点前端框架jQueryVue3 TypeScript类型安全组件化上传组件WebUploader基础版定制版支持文件夹ETag递归解析文件夹结构后端语言JSPSpringBoot 3.0响应式编程Oracle优化数据库MySQLOracle 21cBLOB存储优化分区表对象存储百度OBS腾讯云COS适配不同的分片API规范如需完整项目或调试协助请联系QQ群或留言获取测试账号SQL示例创建数据库配置数据库连接自动下载maven依赖启动项目启动成功访问及测试默认页面接口定义在浏览器中访问数据表中的数据效果预览文件上传文件刷新续传支持离线保存文件进度在关闭浏览器刷新浏览器后进行不丢失仍然能够继续上传文件夹上传支持上传文件夹并保留层级结构同样支持进度信息离线保存刷新页面关闭页面重启系统不丢失上传进度。批量下载支持文件批量下载下载续传文件下载支持离线保存进度信息刷新页面关闭页面重启系统均不会丢失进度信息。文件夹下载支持下载文件夹并保留层级结构不打包不占用服务器资源。示例下载下载完整示例