ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

WebUploader大文件上传优化:CAD图纸分片传输与断点续传实战

WebUploader大文件上传优化:CAD图纸分片传输与断点续传实战 1. 问题拆解CAD图纸上传在央企汽车制造场景里到底卡在哪我最早接手这个改造任务时第一反应是WebUploader这组件不是挺成熟吗分块上传、进度条、MD5校验都有怎么到了我们PLM系统里就成了老大难结果翻了一圈实际使用数据才发现问题的核心不是组件不行而是CAD图纸这种业务文件和普通办公文档在上传特性上根本不是一类东西。先说场景。央企汽车制造企业的研发设计环节用的是三维CAD软件常见的有CATIA、NX、Creo整车装配模型动辄1GB以上单个零件图纸也有几十到几百MB。这些图纸走的是PLM/PDM系统的受控流程设计人员出图、上传系统、走校对审核批准流程每一版都要留痕归档。文件大、版本多、更新时间集中这是CAD图纸上传的第一重压力。第二重压力来自内网环境。很多人以为内网带宽高、传输就快但制造企业的内网往往要承载生产MES数据、办公业务、视频会议等一堆流量而且跨厂区的链路经常抖动。设计部门在上海服务器可能在武汉中间一段专线不稳定是常态。我在改造前的统计数据里看到超过200MB的文件一次上传成功率不到70%经常传到最后几个分片断了前功尽弃只能重来。第三重压力是业务属性。CAD图纸不容忍任何字节差错一个分片丢数据或者合并顺序错乱轻则无法打开重则建模出现隐性的几何错误。所以文件完整性校验必须做但不能用那种让用户等到天荒地老的方式去做。这三重压力叠加起来靠WebUploader的默认配置确实扛不住。组件的底层机制没问题缺的是针对“超大文件不稳定链路严格完整性要求”的业务化改造。这也就定下了整个项目的基调不换组件不做服务端协议重构纯粹用JS在WebUploader的事件钩子里做外科手术。2. 为什么保留WebUploader而不是换上传组件在央企环境里做技术选型第一约束不是性能是合规和系统边界。PLM系统属于核心业务系统它的上传接口经过安全网关、审计日志、文件杀毒扫描等多层链路换组件意味着要动服务端接收逻辑涉及部门多、审批链条长风险不可控。WebUploader本身是百度开源的老牌组件BSD协议在企业内网使用没有任何授权问题并且它的上传模块独立、事件钩子完善这给了纯前端改造很大的操作空间。入手之前我把WebUploader的几个关键机制捋了一遍这部分理解不透后面的改造无从谈起。分块上传的核心流程是文件在前端被切成固定大小的chunk每个chunk作为独立的multipart请求发送服务端按文件唯一标识和chunk序号暂存全部完成后按序号合并。WebUploader暴露了before-send、send、upload-progress、upload-error、upload-success等事件我们可以在这些钩子里插入自定义逻辑。官方对文件完整性的处理方式是计算整个文件的MD5也就是把整个文件读入内存交给SparkMD5计算。这个逻辑在小文件上没问题但CAD图纸几百MB甚至1GB以上时问题就很严重浏览器一次性读入大文件形成巨大的ArrayBuffer内存峰值能到GB级别页面直接假死SparkMD5计算总耗时随文件大小线性增长我实测1.8GB的整车模型光算MD5就要十几分钟。用户体验是点击上传后进度条纹丝不动好半天技术侧是CPU占满、内存告警。另一个痛点是分块参数完全静态。官方配置里chunkSize和threads是固定的但内网链路的实际吞吐是动态变化的。固定大分块链路抖动时重试代价高固定小分块好网络下服务端合并请求数量巨大反而拖慢整体速度。固定并发数也一样网络差时3路并发会加剧丢包。这个静态配置的设计对普通办公文档没问题对超大CAD文件就显得很笨拙。官网还有一个缺失上传状态和业务元数据是脱节的。设计人员上传的图纸需要绑定图号、版本、流程节点官方组件只关心文件本身服务端拿到分片后很难判断这段分片属于哪个版本、哪个流程。这在PLM场景里是个大问题靠URL参数去拼太脆弱传大文件过程中参数很容易丢。基于这些分析改造目标就很清晰了。一是把MD5计算从“全量计算”降级为“分段增量计算关键区指纹校验”让用户点完上传能立刻看到进度。二是把chunkSize和threads做成动态自适应网络好时激进提速链路差时自动降级保稳定。三是给每个分片注入业务元数据让服务端能够感知图纸的版本和流程上下文。四是建立断点续传和自动重试机制刷新页面、断网后能够无缝恢复。3. 核心改造一MD5分段增量计算与关键区指纹校验3.1 为什么全量MD5在CAD场景下不可行MD5计算绕不过去因为它承担两个职责一是在上传前判断文件是否已经在服务器上秒传二是服务端合并后校验文件是否完整一致。但WebUploader默认的整文件读入式计算成本太高这不是组件作者设计失误而是当年主要面向中小文件的定位决定的。到了CAD图纸这个量级必须改算法。我的方案是分段增量计算加关键区指纹。所谓分段增量计算就是不再一次性读入整个文件而是把文件按2MB切块每读入一个块就append到SparkMD5的实例里append完立刻释放这个块的ArrayBuffer让垃圾回收及时回收内存。这样内存峰值被压在一个块的大小范围内页面不会假死CPU使用率也平稳。但增量计算只是解决了内存和界面卡顿计算总耗时并没有减少。于是我又加了关键区指纹校验完整MD5仍然要算但不在上传前算而是在上传完成后由服务端算前端在上传前只采样计算文件的“指纹特征”——文件总大小、文件头部1MB的MD5、文件尾部1MB的MD5、以及均匀抽取中间若干块的短哈希组合。这个指纹的碰撞概率极低足以支撑秒传判断。这里有一个取舍要说清楚。如果你需要绝对的文件唯一性判断比如图纸存在同名但内容微调的场景采样指纹可能判断为“不同文件”而重新上传这是可接受的反过来如果你用采样指纹判断为“相同文件”而跳过上传概率极低但严谨起见我还是会保留服务端合并后的完整MD5校验两道防线双重保险。3.2 分段增量MD5的关键实现分段增量MD5的实现核心是控制内存生命周期。需要注意的坑是SparkMD5.ArrayBuffer的append方法接收ArrayBuffer时内部并不复制数据而是持有引用。如果你把一个分片的ArrayBuffer append进去后立刻让原始变量置空并不能保证内存被释放因为SparkMD5内部可能还持有它。实际上SparkMD5内部确实保留了引用直到end()调用。我的处理方式是从File对象通过file.slice(start, end)读取分片ArrayBufferappend进spark后把slice返回的Blob和ArrayBuffer都置为null并主动触发一次低优先级gc提示。严格来说JS无法强制GC但让大对象失去引用后浏览器会在适当的时候回收。实测经验是2MB分片粒度下页面内存从改造前的1GB以上峰值降到了200MB左右效果非常明显。分片读取和append的代码如下function calcIncrementalHash(file, chunkSize, onProgress) { return new Promise(function(resolve, reject) { var spark new SparkMD5.ArrayBuffer(); var totalSize file.size; var offset 0; var chunkIndex 0; function readNext() { if (offset totalSize) { // 所有分片都已append结束计算 var hash spark.end(); spark null; // 释放spark内部的大对象引用 resolve(hash); return; } var end Math.min(offset chunkSize, totalSize); var blob file.slice(offset, end); var reader new FileReader(); reader.onload function(e) { var arrayBuffer e.target.result; spark.append(arrayBuffer); offset end; chunkIndex; // 关键立刻释放大对象的引用 blob null; e.target.result null; if (onProgress) { onProgress(offset / totalSize); } // 用setTimeout把递归调用从当前调用栈解耦避免栈深问题 setTimeout(readNext, 0); }; reader.onerror function(err) { reject(err); }; reader.readAsArrayBuffer(blob); } readNext(); }); }这个函数接收File对象、分片大小和进度回调返回Promise。我在实际应用里把它包在WebUploader的before-send之前执行计算期间并不过早发起上传请求因为这时候还没有拿到分片指纹。3.3 采样指纹的构造逻辑采样指纹用于秒传预判逻辑不复杂但要对工程场景做调参。我的做法是先读取文件头1MB和尾1MB分别算短哈希再在文件前中后各取3个256KB段拼成一个buffer统一算一个哈希最后加上文件总字节数。组合成一个fingerprint字符串。这里特别强调一下为什么要头尾都取。CAD文件格式里头部通常存版本、单位、坐标系等元信息尾部存模型边界和参数表这两部分是文件最容易因修改而变化的地方。中间部分取采样点是为了捕捉主体几何数据的变动。实际使用中我用整车装配体测试文件稍有修改采样指纹必然变化没有出现过误判“相同”的情况。这个指纹计算耗时通常不超过1秒对秒传判断足够用。实现上不贴完整代码了核心就是复用上面的读取逻辑只不过不是全部append到同一个spark而是分块计算后做拼接。4. 核心改造二动态分片与并发自适应4.1 静态分片参数在CAD场景下的两难WebUploader官方推荐的chunkSize通常固定4MB或5MBthreads固定3。这个配置在普通办公文件下问题不大但CAD图纸的体量彻底放大了静态配置的缺陷。如果分片太小比如2MB一个1GB的文件会产生512个分片请求。每个请求都要走一次HTTPS握手、上传网关鉴权、杀毒扫描、文件暂存。服务端压力巨大而且很多分片请求的时间主要耗在链路往返上而不是数据传输上整体吞吐反而下降。如果分片太大比如50MB虽然请求数量少了但一旦链路出现抖动一个分片失败重传的代价高达几十MB数据量并且大分片在网关层触发超时的概率也更高。我在前期排查中见过服务端nginx报client intended to send too large body的案例就是分片超过网关限值导致的。所以正确的做法是动态决定分片大小。我的逻辑非常简单粗暴按文件体积划分区间再结合实测网络吞吐微调。function calcChunkSize(fileSize) { if (fileSize 100 * 1024 * 1024) { return 2 * 1024 * 1024; // 100MB以下用2MB分片 } else if (fileSize 500 * 1024 * 1024) { return 4 * 1024 * 1024; // 100MB~500MB用4MB分片 } else if (fileSize 1024 * 1024 * 1024) { return 8 * 1024 * 1024; // 500MB~1GB用8MB分片 } else { return 16 * 1024 * 1024; // 超过1GB用16MB分片 } }这个区间划分不是拍脑袋定的。2MB分片在小文件场景下反馈快用户可以快速看到进度条走动4MB是WebUploader生态里验证最多的值8MB和16MB是为超大文件减少请求数量同时兼顾网关层的body大小限制一般企业网关都能扛16MB的multipart请求。4.2 并发窗口的自适应调整并发数不能只看静态网络测试内网链路的实时状态才是关键。我的做法是维护一个滑动窗口记录最近15个分片的传输耗时。窗口内平均耗时上升说明链路开始拥塞并发数减半平均耗时稳定下降则逐步放开并发上限。核心代码大致是这样var concurrency 3; // 当前并发数 var MAX_CONCURRENCY 5; // 最大并发 var MIN_CONCURRENCY 1; // 最小并发 var speedWindow []; // 滑动窗口只保留最近15个分片的耗时 var WINDOW_SIZE 15; function recordChunkDuration(duration) { speedWindow.push(duration); if (speedWindow.length WINDOW_SIZE) { speedWindow.shift(); } var avg speedWindow.reduce(function(a, b) { return a b; }, 0) / speedWindow.length; if (speedWindow.length WINDOW_SIZE) { if (avg 5000) { // 平均耗时超过5秒链路明显有问题快速降级 concurrency Math.max(MIN_CONCURRENCY, concurrency - 1); } else if (avg 1500 concurrency MAX_CONCURRENCY) { // 链路稳定且较快尝试提并发 concurrency Math.min(MAX_CONCURRENCY, concurrency 1); } } }这个降级策略的关键点是必须设置MIN_CONCURRENCY为1。不要因为网络差就把并发降到0极端情况下用户会以为上传挂了。保留1路并发即使慢但进度条始终在动用户心理体验完全不同。还有一个工程师容易忽略的细节动态调整并发数不能直接把WebUploader正在执行的xhr列表砍掉。只能影响新的分片请求是否启动。所以实现上要拦截WebUploader的请求调度逻辑在内部维护一个“可启动”队列当前活跃请求数低于并发目标时才从队列里取下一个分片。4.3 业务元数据注入CAD图纸上传必须绑定图号和版本。我在before-send钩子里对每个分片注入额外的formData字段服务端无需改动协议就能解析到上下文。uploader.on(before-send, function(file, data) { // data是分片请求的postData对象 data.dwgNo $scope.currentDrawing.dwgNo; // 图号 data.version $scope.currentDrawing.version; // 版本号 data.processNode $scope.currentDrawing.node; // 流程节点 data.taskId $scope.currentTaskId; // 流程任务ID return true; });这套元数据注入看起来简单但它解决了一个长期存在的业务痛点之前图纸上传到服务端后运维人员难以追溯某个分片属于哪个流程节点。现在服务端能够感知分片所属的版本和流程出现问题时可以直接按图号、版本号定位不需要前端再拼日志。5. 核心改造三断点续传与自动重试的完整实现5.1 断点续传的状态记录策略WebUploader官方支持断点续传但默认基于文件MD5而文件MD5正是我们要避免全量计算的所以必须自己实现一套轻量状态记录逻辑。思路是牺牲极小的存储代价换取进度恢复能力。每次成功上传一个分片就在localStorage里记录一条键值对。键的组成尽量稳定图号版本号文件最后修改时间戳。值记录已上传的分片序号列表。这样下次同一用户在同一浏览器上传同一文件时通过文件指纹不是全量MD5命中记录直接跳过已上传的分片。需要注意的坑是localStorage容量有限记录上百个分片序号字符串占用不大但如果没有及时清理长时间积累会产生垃圾数据。我加了一个清理时机文件全部上传完成并收到服务端合并成功回调后删除对应的localStorage键。另外服务端完成合并后分片暂存区会被清理即便前端状态记录还在服务端也拼不出完整文件所以要以服务端合并状态为准。5.2 指数退避重试机制CAD图纸上传过程中链路抖动导致的单个分片失败非常常见。官方默认行为是直接判定该分片失败用户手动点重传。在大文件场景下这个交互等于让用户守在上传页面盯进度条体验极差。我的改造方案是拦截upload-error事件对可重试错误做指数退避重试。退避间隔从1秒开始每次失败翻倍最多重试5次。重试上限次数耗尽才真正判定上传失败并给出明确的错误提示。这里有个判断不能漏掉不是所有错误都该重试。HTTP 413请求体过大、403鉴权失败、400业务参数错误属于不可恢复错误重试再多次也是浪费流量和时间。只有408、500、502、504、网络层错误这类才适合自动重试。var retryMap {}; // 分片ID - 当前重试次数 uploader.on(upload-error, function(file, xhr) { var chunk getChunkFromXhr(xhr); // 从请求上下文解析分片信息 var status xhr.status; var retryable [408, 500, 502, 503, 504, 0].indexOf(status) ! -1; if (!retryable) { // 不可重试错误交回给官方逻辑处理 return; } var chunkId file.id _ chunk.chunkIndex; var retryCount retryMap[chunkId] || 0; if (retryCount 5) { delete retryMap[chunkId]; // 重试次数耗尽标记失败并通知用户 uploader.trigger(failAfterRetries, file, chunk); return; } var delay Math.pow(2, retryCount) * 1000; // 1s, 2s, 4s, 8s, 16s retryMap[chunkId] retryCount 1; setTimeout(function() { uploader.retry(file, chunk); // 重传这一个分片 }, delay); });重试期间进度条要明确显示“网络波动自动重试第N次”不要让用户反复看到进度条回退。进度条回退是WebUploader默认逻辑里最伤用户体验的设计之一我改造后进度条只前进不后退重试产生的时间消耗体现在等待动画里。5.3 页面刷新后的完整恢复链路断点续存要真正可用必须覆盖“页面刷新”这个最残酷的场景。设计人员在CAD软件里导出一个模型文件后上传到一半不小心关了浏览器或者公司办公网上网拨号掉线重新打开页面后应能继续上传直至完成。实现上我给每个上传任务生成一个taskId在页面初始化时扫描localStorage如果发现未完成的上传记录就弹出恢复提示框列出图纸名称和进度用户点击恢复后无缝继续。恢复时不需要重新计算指纹直接从localStorage读取记录的分片状态从第一个缺失的分片继续发。还要照顾一个细节恢复上传前需要向服务端确认哪些分片其实已经持久化了。因为前端记录的是“已成功发送”的分片但服务端可能因为暂存区清理策略或者异常崩溃丢失了部分数据。我的做法是调用一个轻量的服务端接口传入taskId返回已接收的分片序号集合再和本地记录做差集确定真正需要重传的分片。这一步能够把客户端状态和服务端状态对齐避免出现数据不一致导致的合并失败。6. 服务端配合要点与常见问题排查6.1 服务端分片接收的正确姿势前端改造再完善服务端配合不到位整个链路还是不稳。这里讲几个服务端必须做好的基础点虽然不是本次改造的前端内容但排查问题时绕不开。服务端接收分片时每个分片请求都应携带文件唯一标识、分片序号、总分片数、业务元数据。文件唯一标识我用的是前端生成的taskId而不是文件名。因为央企PLM系统里同一张图纸有多个版本文件名相同但内容不同taskId能精确唯一地锁定一个上传任务。分片到达服务端后服务端要做的第一件事不是写磁盘而是校验分片序号是否合法。我遇到过客户端并发上传时顺序错乱的问题几个分片乱序到达服务端如果不校验合并时就会按到达顺序拼接文件直接损坏。正确做法是每个分片都独立暂存以分片序号为文件名的一部分合并时按序号排序重组。这个逻辑虽然基础但在一些临时对接的网关模块里很容易被忽略。服务端还需要做幂等接收。客户端自动重试时某个分片可能已经成功写入但因为响应超时导致客户端认为失败从而重试发送。服务端要能够识别重复分片并直接返回成功而不是报错。我见过因为这点没处理导致断点续传恢复后永远卡在某个分片上。6.2 改造过程中的经典翻车场景第一个翻车点是动态分片和WebUploader内部状态管理冲突。WebUploader在文件加入队列后就会确定分片索引如果你中途改变chunkSize已生成的分片索引全部失效服务端合并直接错乱。我的处理方式是文件进入队列时一次性计算并锁定chunkSize整个生命周期不允许变更。动态调整只是在计算未来新增超大文件的初始分片参数时生效而不是在传输中途改。第二个翻车点是并发自适应导致的内存飙升。原因是最初实现时我在提高并发数的同时也放大了每个并发分片的读取缓冲区结果多个分片同时读入内存峰值又回到了800MB。后来限定每个分片的ArrayBuffer读取都要独立且即读即传读完后立刻置空引用这才把内存压住。第三个翻车点是浏览器兼容性。央企内网还有不少老版本Chrome和Edge甚至有部分IE11的残存虽然微软不维护了但工业软件站点很多依赖老内核WebView。FileReader和ArrayBuffer在IE11下坑很多分片读取稍大就报内存溢出。我的方案是做一个运行环境检测检测到老浏览器时强制使用2MB分片并禁用并发自适应固定单路上传。稳定比速度重要这是央企系统改造的铁律。6.3 常见问题速查表这里整理一份我在改造中遇到的高频问题和排查方法方便你直接对照处理。现象可能原因排查与解决办法点击上传后长时间没有进度全量MD5计算未改造大文件卡在读盘检查是否启用了分段增量MD5确认chunkSize有效页面内存持续攀升SparkMD5持有分片引用未释放检查append后是否置空ArrayBuffer引用减小读取分片粒度进度条反复回退官方默认失败逻辑触发用自定义重试逻辑替代进度只前进不后退恢复上传后卡在99%不动服务端缺失某个分片但客户端状态认为已传增加向服务端查询实际分片集合的同步接口服务端合并时文件损坏并发上传导致分片乱序合并服务端按分片序号排序后再合并不要按到达顺序拼网关返回413 Request Entity Too Large单个分片超过网关body限值调低该场景chunkSize或联系网关调整上限重试风暴刷爆服务端接口指数退避被绕过立即重试确认重试路径走了退避逻辑且不可重试错误不触发重试上传成功后本地状态未清理localStorage键被长期占用在upload-success或merge-success后主动删除localStorage记录老浏览器上传直接崩溃ArrayBuffer读取内存溢出强制2MB分片、单并发、禁止增量MD5的高级采样逻辑第四点要特别强调。我在测试阶段遇到过用户传了99%进度条长时间停滞最后报错。查下来是恢复续传时客户端误以为某个分片已传成功实际服务端暂存分区在那个时间点做了清理。加了服务端状态同步后问题彻底解决。这个坑建议你在设计恢复链路时就避开不要等出问题再补。第五点也是老生常谈但外包团队对接PLM系统时频繁踩。原理不复杂但架不住接口文档没写清楚、合并逻辑用了个简单for循环按到达顺序拼接。前文已经说明这里不再展开。6.4 改造上线后的运营观测建议改造完成不是终点上线后要盯着几项核心指标来验证效果。我用的指标有四个大文件平均上传耗时、上传失败率、用户中断后恢复成功率、服务端暂存分片总量。耗时指标直接对标改造前数据。失败率按日粒度统计重点看大文件的一次成功率。恢复成功率是断点续存是否真正可用的判据我对接下来的目标是达到95%以上。暂存分片总量反映服务端存储压力动态分片方案下超大文件的分片数量比固定小分片方案明显下降。还有一个观测点容易被忽略上传并发数峰值。如果大量设计人员在同一时段上传图纸自适应并发的上限直接决定服务端峰值压力。我在内网压测时发现5路并发是网关层的甜蜜点超过5路会导致网关连接排队整体吞吐不升反降。所以在并发上限配置上我最终是硬性锁定了5没有让它无限往上探。内网网关的瓶颈往往不在带宽而在连接数这点和公网场景差别很大。另外建议在WebUploader的upload-progress事件里埋一个基于时间戳的埋点把进度数据回传到日志平台。不要小看这个埋点它能让你在用户反馈“上传慢”时快速区分是MD5计算阶段慢、还是分片传输阶段慢、还是合并阶段慢。我改造后的经验是至少有一半的“上传慢”投诉发生在服务端合并阶段前端再优化也解决不了需要推动后端做异步合并或分布式文件存储改造。7. 改造落地后的数据与体验对比改造在测试环境跑通后我们选了一个真实业务场景做了三周灰度设计一部两个科室提交整车装配图纸文件集中在500MB到2GB之间。对比改造前后的上传数据效果差异非常明显。选取一个典型的1.8GB整车CAD装配体文件为例指标改造前改造后上传前MD5计算耗时约18分钟页面假死约40秒采样指纹页面流畅分片大小固定5MB共369个分片动态16MB共115个分片平均耗时约4小时经常中断约1.5小时链路好时更快失败率约30%失败后手动重传低于3%自动重试断点恢复服务端暂存请求数369次115次网关压力显著下降用户体验点击上传后无反馈进度条卡顿秒级出进度波动时显示自动重试这个对比最能说明问题的是分片数量的大幅下降。369次网关请求缩减到115次意味着防病毒扫描、安全审计、暂存I/O这些服务端开销整体减少接近70%。网关层不再成为瓶颈后传输速率才真正体现出来。我印象很深的一个案例是设计一部的一位工程师他要上传一个CATIA装配模型2.1GB图形工作站上导出模型用了半小时以前每次上传都是煎熬。改造后他的反馈是“终于可以在上传期间去继续建模了”因为断点续传和自动重试让他不需要一直盯着页面。还有个业务上的附带收益以前图纸上传失败率太高很多设计人员为了赶进度绕过PLM流程直接用聊天工具传图纸形成大量不受控的图档散落在外这对央企的图文档管控是很大的隐患。上传的稳定性提升后流程内提报率回升这算超出技术预期的管理价值。8. 经验总结这轮JS改造的得与失这套改造做完我最大的体会是优化一个老牌开源组件第一原则永远是“能用最小改动解决问题就别想着重写”。WebUploader的官方机制虽然在大文件场景下暴露出种种不适但它的事件钩子系统设计得很开放给了我们在不碰源码、不碰服务端协议的前提下做精准改造的空间。改造过程中有几个判断我觉得做对了。一是把MD5计算从全量改为分段增量加采样指纹没有为了省事而直接放弃秒传能力。二是在动态并发中预留了最小并发1的兜底在最坏的链路上用户仍能看到进度条在走。三是引入了向服务端同步实际分片状态的恢复流程这避免了大量“表面上恢复、实际卡死”的隐性bug。有一个判断我想提出来供同行参考不要把并发自适应设计得过于激进。网上很多方案追求“AI动态调优”实际在制造业内网里链路波动是有规律的比如上午MES数据高峰、午间休息时段稳定简单的时间段预设可能在工程实践中比实时自适应更实用。我现在的实现是两者结合默认按时间段预设基线的并发和分片实时窗口只做小幅度修正不搞大幅波动效果稳定得多。另外一个容易被低估的工程点是进度展示的“不可逆”设计。我改造后的进度条只前进不后退即使某个分片失败进入重试进度也保持不变直到重试成功后才继续前进。用户看到的是一个不断前进的进度条而不是反复横跳的百分比。这个设计对信任感的影响比技术性能改善更直接。后续如果还要继续优化这条路我会优先考虑两件事一是把采样指纹和增量哈希的计算放到Web Worker里去做彻底解放主线程让设计人员的浏览器在等待时还能顺畅操作CAD客户端CAD桌面软件和浏览器抢CPU资源非常常见二是把上传进度推送给后端通过WebSocket或者其他长连接机制同步到PLM系统的进度页面让流程发起人不用守在工位上也能看到图纸的传输状态。这套改造方案整体上不复杂复杂的是在正确的位置插入正确的逻辑。WebUploader这类老组件在央企系统里还会存在很久摸清它的事件机制和短板用JS在上层做文章是一种实用且稳妥的路线。希望在类似场景里趟路的同行能少踩我踩过的那些坑。
返回列表