Janus-Pro-7B教育评估:学生作业图片批改+个性化反馈文字生成
2026/4/2 18:46:41
作为福建IT行业软件公司项目负责人,针对贵司提出的大文件传输需求,我进行了全面分析,发现以下几个核心挑战:
[客户端] ←HTTPS→ [Nginx负载均衡] ←→ [应用服务器集群] ←→ [MySQL/SQL Server/Oracle] ↑ ↓ [阿里云OSS/私有存储]// FileUploadController.java (SpringBoot示例)@PostMapping("/uploadChunk")publicResponseEntityuploadChunk(@RequestParam("file")MultipartFilefile,@RequestParam("chunkNumber")intchunkNumber,@RequestParam("totalChunks")inttotalChunks,@RequestParam("identifier")Stringidentifier,@RequestParam("filename")Stringfilename,@RequestParam("relativePath")StringrelativePath){// 校验分块if(file.isEmpty()){returnResponseEntity.badRequest().body("Empty chunk");}// 存储分块到临时目录StringtempDir=getTempDir(identifier);StringchunkFilename=chunkNumber+".part";FilechunkFile=newFile(tempDir,chunkFilename);try{file.transferTo(chunkFile);// 记录分块信息到数据库uploadService.recordChunk(identifier,filename,relativePath,chunkNumber,totalChunks);// 检查是否所有分块已上传if(uploadService.checkAllChunksUploaded(identifier,totalChunks)){returnassembleFile(identifier,filename,relativePath);}returnResponseEntity.ok().body("Chunk uploaded");}catch(IOExceptione){returnResponseEntity.status(500).body("Upload failed");}}privateResponseEntityassembleFile(Stringidentifier,Stringfilename,StringrelativePath){StringtempDir=getTempDir(identifier);FiletempDirFile=newFile(tempDir);// 获取所有分块文件File[]chunks=tempDirFile.listFiles((dir,name)->name.matches("\\d+\\.part"));if(chunks==null||chunks.length==0){returnResponseEntity.status(500).body("No chunks found");}// 按分块编号排序Arrays.sort(chunks,Comparator.comparingInt(f->Integer.parseInt(f.getName().split("\\.")[0])));// 创建目标文件StringsavePath=getSavePath(relativePath);FiledestFile=newFile(savePath,filename);try(FileOutputStreamfos=newFileOutputStream(destFile,true)){// 合并所有分块for(Filechunk:chunks){Files.copy(chunk.toPath(),fos);chunk.delete();// 删除已合并的分块}// 记录完整文件信息uploadService.recordCompleteFile(identifier,filename,relativePath,destFile.length());returnResponseEntity.ok().body("File assembled");}catch(IOExceptione){returnResponseEntity.status(500).body("Assembly failed");}}// LargeFileUploader.vueexportdefault{data(){return{fileList:[],chunkSize:5*1024*1024,// 5MBconcurrentLimit:3,activeUploads:0}},methods:{handleFileChange(e){constfiles=Array.from(e.target.files);this.fileList=files.map(file=>({id:this.generateFileId(file),fileObject:file,name:file.name,relativePath:file.webkitRelativePath||'',size:file.size,progress:0,chunks:Math.ceil(file.size/this.chunkSize),uploadedChunks:0}));},generateFileId(file){return`${file.name}-${file.size}-${file.lastModified}-${Math.random().toString(36).substr(2,9)}`;}}}// ie8-wrapper.js(function(){// 添加File API polyfillif(typeofwindow.File==='undefined'){window.File=function(){};}// 添加FormData polyfillif(typeofFormData==='undefined'){window.FormData=function(){this.data=[];this.append=function(key,value){this.data.push({key:key,value:value});};this._getData=function(){returnthis.data;};};}// XMLHttpRequest增强varoriginalXHROpen=XMLHttpRequest.prototype.open;XMLHttpRequest.prototype.open=function(method,url,async,user,password){// 保存请求信息供send方法使用this._method=method;this._url=url;originalXHROpen.apply(this,arguments);};varoriginalXHRSend=XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.send=function(data){if(datainstanceofFormData){// 处理FormData polyfillvarfd=data._getData();varboundary='----WebKitFormBoundary'+Math.random().toString(36).substr(2);varbody='';for(vari=0;i<fd.length;i++){body+='--'+boundary+'\r\n';body+='Content-Disposition: form-data; name="'+fd[i].key+'"';if(fd[i].valueinstanceofFile){body+='; filename="'+fd[i].value.name+'"\r\n';body+='Content-Type: '+fd[i].value.type+'\r\n\r\n';// 实际处理中需要读取文件内容body+='[FILE_CONTENT]\r\n';}else{body+='\r\n\r\n'+fd[i].value+'\r\n';}}body+='--'+boundary+'--\r\n';this.setRequestHeader('Content-Type','multipart/form-data; boundary='+boundary);data=body;}originalXHRSend.call(this,data);};})();// IE8MultipartFilter.javapublicclassIE8MultipartFilterimplementsFilter{@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{HttpServletRequesthttpRequest=(HttpServletRequest)request;StringcontentType=httpRequest.getContentType();// 检查是否是IE8的特殊multipart请求if(contentType!=null&&contentType.contains("multipart/form-data")){StringuserAgent=httpRequest.getHeader("User-Agent");if(userAgent!=null&&userAgent.contains("MSIE 8")){// 对IE8的特殊处理HttpServletRequestwrappedRequest=newIE8CompatibleMultipartHttpServletRequest(httpRequest);chain.doFilter(wrappedRequest,response);return;}}chain.doFilter(request,response);}}CREATETABLE`file_uploads`(`id`bigint(20)NOTNULLAUTO_INCREMENT,`identifier`varchar(64)NOTNULLCOMMENT'文件唯一标识',`filename`varchar(255)NOTNULLCOMMENT'原始文件名',`filepath`varchar(512)NOTNULLCOMMENT'存储路径',`relative_path`varchar(512)DEFAULT''COMMENT'相对路径(用于文件夹结构)',`filesize`bigint(20)NOTNULLCOMMENT'文件大小(字节)',`status`tinyint(4)NOTNULLDEFAULT'0'COMMENT'状态:0-上传中,1-已完成,2-已取消',`created_at`datetimeNOTNULLDEFAULTCURRENT_TIMESTAMP,`completed_at`datetimeDEFAULTNULL,`user_id`bigint(20)DEFAULTNULLCOMMENT'上传用户ID',`encryption_type`varchar(20)DEFAULTNULLCOMMENT'加密类型:SM4/AES',`encryption_key`varchar(512)DEFAULTNULLCOMMENT'加密密钥(加密存储)',PRIMARYKEY(`id`),UNIQUEKEY`idx_identifier`(`identifier`),KEY`idx_user_status`(`user_id`,`status`))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COMMENT='文件上传主记录';CREATETABLE`file_chunks`(`id`bigint(20)NOTNULLAUTO_INCREMENT,`upload_id`bigint(20)NOTNULLCOMMENT'关联file_uploads.id',`identifier`varchar(64)NOTNULLCOMMENT'文件唯一标识',`chunk_number`int(11)NOTNULLCOMMENT'分块编号',`chunk_size`int(11)NOTNULLCOMMENT'分块大小(字节)',`chunk_path`varchar(512)DEFAULTNULLCOMMENT'分块存储路径',`upload_time`datetimeNOTNULLDEFAULTCURRENT_TIMESTAMP,`checksum`varchar(64)DEFAULTNULLCOMMENT'分块校验和',PRIMARYKEY(`id`),UNIQUEKEY`idx_identifier_chunk`(`identifier`,`chunk_number`),KEY`idx_upload_id`(`upload_id`))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COMMENT='文件分块记录';CREATETABLE`folder_structures`(`id`bigint(20)NOTNULLAUTO_INCREMENT,`upload_id`bigint(20)NOTNULLCOMMENT'关联file_uploads.id',`parent_id`bigint(20)DEFAULTNULLCOMMENT'父文件夹ID',`name`varchar(255)NOTNULLCOMMENT'文件夹名',`relative_path`varchar(512)NOTNULLCOMMENT'相对路径',`created_at`datetimeNOTNULLDEFAULTCURRENT_TIMESTAMP,PRIMARYKEY(`id`),KEY`idx_upload_id`(`upload_id`),KEY`idx_parent_id`(`parent_id`),KEY`idx_relative_path`(`relative_path`(255)))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COMMENT='文件夹结构记录';client → [Nginx负载均衡] → [应用服务器1] ↘→ [应用服务器2] ↘→ [应用服务器3]基于贵司需求,我们提供以下两种合作模式:
买断授权方案(推荐)
按项目授权方案
导入到Eclipse:点南查看教程
导入到IDEA:点击查看教程
springboot统一配置:点击查看教程
NOSQL示例不需要任何配置,可以直接访问测试
选择对应的数据表脚本,这里以SQL为例
up6/upload/年/月/日/guid/filename
支持离线保存文件进度,在关闭浏览器,刷新浏览器后进行不丢失,仍然能够继续上传
支持上传文件夹并保留层级结构,同样支持进度信息离线保存,刷新页面,关闭页面,重启系统不丢失上传进度。
支持文件批量下载
文件下载支持离线保存进度信息,刷新页面,关闭页面,重启系统均不会丢失进度信息。
支持下载文件夹,并保留层级结构,不打包,不占用服务器资源。
点击下载完整示例