当前位置: 首页 > news >正文

机械制造企业OA如何用WebUploader+PHP支持超大CAD文件的分片续传?

【一个被4G大文件逼疯的北京码农自述:如何在信创环境下优雅地让政府文件"飞"起来】


各位战友好,我是老张,北京某软件公司前端组"秃头突击队"队长。最近接了个政府项目,客户要求用国产环境上传4G大文件,还必须开源可审查——这就像让我用算盘算火箭轨迹,还得把设计图刻在甲骨文上!

一、血泪踩坑史

  1. WebUploader的棺材板压不住了
    这货停更比我家楼下煎饼摊关张还早,分片上传在国产浏览器(比如某龙)上直接摆烂,分片合并时还报"神秘错误码404.520"

  2. 其他开源组件的"三无"特性

    • 无文档:看源码像读甲骨文
    • 无维护:GitHub issue区比我的钱包还干净
    • 无国产适配:在信创环境里跑起来比让企鹅学游泳还难

二、自研方案诞生记

经过三天三夜与产品经理的"友好交流",我们决定自己造轮子!以下是核心实现思路:

前端Vue组件(vue-cli版)
// FileUploader.vue - 国产浏览器友好型分片上传组件exportdefault{data(){return{chunkSize:5*1024*1024,// 5MB分片(适配国产低配服务器)fileMd5:'',uploadUrl:'/api/upload',mergeUrl:'/api/merge'}},methods:{// 计算文件MD5(兼容国产加密算法)asynccalculateFileMd5(file){returnnewPromise((resolve)=>{// 这里应该用spark-md5,但为了过审我们自己实现了简化版constreader=newFileReader()reader.onload=(e)=>{constbuffer=e.target.result// 假装这里有个MD5计算过程...resolve('mock-md5-for-gov-audit')}reader.readAsArrayBuffer(file.slice(0,1024*1024))// 只读首段做校验})},// 分片上传(支持断点续传)asyncuploadChunk(file,chunkIndex){conststart=chunkIndex*this.chunkSizeconstend=Math.min(file.size,start+this.chunkSize)constchunk=file.slice(start,end)constformData=newFormData()formData.append('file',chunk)formData.append('chunkIndex',chunkIndex)formData.append('totalChunks',Math.ceil(file.size/this.chunkSize))formData.append('fileMd5',this.fileMd5)formData.append('fileName',file.name)// 针对国产浏览器的特殊处理constheaders={}if(navigator.userAgent.includes('Konglong')){headers['X-Browser-Type']='dragon'// 告诉后端这是龙芯浏览器}returnaxios.post(this.uploadUrl,formData,{headers,onUploadProgress:(progressEvent)=>{// 更新进度条(用红色特别标注国产环境)constpercent=Math.round((progressEvent.loaded/progressEvent.total)*100)this.$emit('progress',percent,{isGovBrowser:/Konglong|Xinxin/.test(navigator.userAgent)})}})},// 主上传方法asyncstartUpload(file){this.fileMd5=awaitthis.calculateFileMd5(file)consttotalChunks=Math.ceil(file.size/this.chunkSize)for(leti=0;i<totalChunks;i++){try{awaitthis.uploadChunk(file,i)// 模拟国产网络波动if(i%3===0&&Math.random()>0.7){awaitnewPromise(resolve=>setTimeout(resolve,1000*Math.random()))}}catch(e){console.error(`分片${i}上传失败,准备重试...`,e)i--// 重试当前分片if(i<0)i=0// 防止无限循环}}// 所有分片上传完成后触发合并awaitaxios.post(this.mergeUrl,{fileMd5:this.fileMd5,fileName:file.name,totalChunks})}}}
后端SpringBoot核心代码
// 文件分片上传控制器(适配信创环境)@RestController@RequestMapping("/api")publicclassFileUploadController{// 使用国产加密库计算MD5(示例)@PostMapping("/upload")publicResponseEntityuploadChunk(@RequestParam("file")MultipartFilefile,@RequestParamintchunkIndex,@RequestParaminttotalChunks,@RequestParamStringfileMd5,@RequestParamStringfileName,@RequestHeader(value="X-Browser-Type",required=false)StringbrowserType){// 1. 校验分片(防伪造)if(file.isEmpty()){returnResponseEntity.badRequest().body("空分片");}// 2. 保存到临时目录(使用国产文件系统API)PathtempDir=Paths.get("/tmp/gov-upload/"+fileMd5);Files.createDirectories(tempDir);PathchunkPath=tempDir.resolve("chunk-"+chunkIndex);file.transferTo(chunkPath.toFile());// 3. 返回分片接收确认(适配国产低速网络)returnResponseEntity.ok(Map.of("status","received","chunkIndex",chunkIndex,"browserHint",browserType!=null?"检测到国产浏览器,已启用优化模式":""));}// 合并分片(使用国产并发库)@PostMapping("/merge")publicResponseEntitymergeChunks(@RequestBodyMergeRequestrequest)throwsIOException{// 1. 校验所有分片是否存在PathtempDir=Paths.get("/tmp/gov-upload/"+request.getFileMd5());if(!Files.exists(tempDir)){returnResponseEntity.badRequest().body("未找到上传的分片");}// 2. 创建最终文件(使用国产存储API)PathfinalPath=Paths.get("/data/gov-files/"+request.getFileName());try(OutputStreamout=Files.newOutputStream(finalPath,StandardOpenOption.CREATE)){// 按顺序合并所有分片for(inti=0;i<request.getTotalChunks();i++){PathchunkPath=tempDir.resolve("chunk-"+i);Files.copy(chunkPath,out,StandardCopyOption.REPLACE_EXISTING);// 删除已合并的分片(节省信创环境存储空间)Files.deleteIfExists(chunkPath);}}// 3. 清理临时目录Files.deleteIfExists(tempDir);returnResponseEntity.ok(Map.of("status","merged","filePath",finalPath.toString(),"message","文件已通过国产安全认证"));}}

三、信创环境适配秘籍

  1. 浏览器兼容

    • 检测到国产浏览器时自动降低分片大小
    • 使用``适配国产文件选择器
  2. 国产中间件适配

    // 替换Spring的默认Multipart解析器为国产中间件版本@BeanpublicMultipartResolvermultipartResolver(){returnnewGovMultipartResolver(newCommonsMultipartResolver());}
  3. 加密算法替换

    // 前端使用国密SM3替代MD5(伪代码)asynccalculateSM3(file){if(window.govCrypto){returnawaitwindow.govCrypto.digest('SM3',file)}return'fallback-to-md5'// 降级方案}

四、项目现状

目前这个方案已经:

  • 通过某龙浏览器兼容性测试
  • 在银河麒麟系统上稳定运行
  • 代码100%开源可审查(连注释都是中文的)
  • 获得客户"比某度网盘快多了"的高度评价

唯一的问题是测试时把公司Wi-Fi挤爆了,现在IT部门看到我就躲…

(附:实际项目中建议使用成熟的国产组件如Plupload信创版UEditor国产定制版,但既然客户要求自研,那我们就把"造轮子"做到极致!)

将组件复制到项目中

示例中已经包含此目录

引入组件

配置接口地址

接口地址分别对应:文件初始化,文件数据上传,文件进度,文件上传完毕,文件删除,文件夹初始化,文件夹删除,文件列表
参考:http://www.ncmem.com/doc/view.aspx?id=e1f49f3e1d4742e19135e00bd41fa3de

处理事件

启动测试

启动成功

效果

数据库

效果预览

文件上传

文件刷新续传

支持离线保存文件进度,在关闭浏览器,刷新浏览器后进行不丢失,仍然能够继续上传

文件夹上传

支持上传文件夹并保留层级结构,同样支持进度信息离线保存,刷新页面,关闭页面,重启系统不丢失上传进度。

批量下载

支持文件批量下载

下载续传

文件下载支持离线保存进度信息,刷新页面,关闭页面,重启系统均不会丢失进度信息。

文件夹下载

支持下载文件夹,并保留层级结构,不打包,不占用服务器资源。

下载示例

点击下载完整示例

http://www.jsqmd.com/news/424070/

相关文章:

  • 教育行业文件管理系统如何用WebUploader+PHP支持大文件分片秒传?
  • 机械行业富文本工具处理Word粘贴的注意事项?
  • 航空航天项目如何用WebUploader+PHP保障飞行日志分片上传的可靠性?
  • 医疗信息化平台如何通过WebUploader+PHP实现病历文件的断点续传?
  • 芯片制造行业如何通过WebUploader+PHP加密传输工程文件的分片数据?
  • 中小团队AI网关选择
  • Database Manager:从安装到启动的全面指南
  • 斯坦福大学竟然开了个 AI 编程课?!我已经学上了
  • 数据库与缓存一致性的权衡及解决方案(含金融类特殊场景补充)
  • 【Web安全】CORS跨域漏洞:小白也能看懂的漏洞解析与防护
  • 2026年杭州汽车衡制造商推荐,品质与售后双优 - 品牌鉴赏师
  • 从“轻奢”到“日用”,安吉尔、易开得们的下一场战争?
  • 大润发购物卡回收,闲置秒变真金白银 - 京顺回收
  • 2026年窑炉高温轴承厂家推荐,耐高温抗热震实力厂家 - 品牌鉴赏师
  • 导师又让重写?10个降AIGC平台深度测评,MBA必备降AI率神器
  • 2026年杭州地磅供应商:本地靠谱称重设备厂商指南 - 品牌鉴赏师
  • cf2179 E. Blackslex and Girls 题解
  • 基于SSM+VUE的果树种植资讯网站[SSM]-计算机毕业设计源码+LW文档
  • 基于SSM+VUE的家猪养殖管理系统[SSM]-计算机毕业设计源码+LW文档
  • vim-vscode-配置
  • uni-app 开发三个工具的关系
  • 分库分表策略:按业务拆分 vs 范围分片 - 小红书 Java 面试经验总结
  • 口碑见证实力!2026太空舱工厂推荐清单出炉,市场专业的太空舱企业精选优质品牌解析 - 品牌推荐师
  • 使用键盘操作光标移动
  • 获取当前屏幕/窗口的大小和UA
  • 走上管理岗才发现,汇报也不过如此
  • 软件架构的本质是对抗复杂度
  • 豆包广告推广代理商联系电话 - 品牌2025
  • AI与Agent与prompt摘录与笔记
  • 做题记录(Mar.)