资讯动态

WebUploader分片上传与断点续传在弱网环境中的实践

发布时间:2026/10/14 5:32:59 来源:尧图企业网站定制
1. 农田监控图片上云为什么会想到WebUploader这套方案先交代一下背景。我在做农情监测类项目时遇到一个非常现实的需求分布在农田各个区域的摄像终端每隔一段时间就会拍摄现场图片图片需要回传到服务端再同步到云端平台供作物长势分析、虫情识别和墒情预警模块使用。问题在于农田现场的弱网环境非常普遍2G网络、信号漂移、基站切换、短暂掉线都是家常便饭。生产环境实测一张几MB的现场图一次性走普通POST上传失败率相当高而且一旦中途断掉就白传了前端用户看到的是进度条卡住后台收到的是残缺文件。当时项目技术栈已经定了Vue2后端是Java栈云存储用的对象存储服务。选型的时候也对比过其他方案比如直接用XMLHttpRequest手写分片逻辑或者用官方SDK自带的分片上传能力。但考虑到两个现实约束一是现场终端有相当一部分是老旧设备浏览器内核版本不高需要兼容低版本内核二是项目时间紧没有太多精力去自研一套完整的上传队列和重试机制。最后选了WebUploader这个组件本身封装了分片、并发、重试、进度反馈等能力在传统后台管理类项目里用得非常多资料也相对好找。这里要说明一下WebUploader并不直接提供断点续传它提供的是“分片上传手动控制上传时机”的基础能力。断点续传效果是靠“分片标识已传分片查询跳过已传分片”这套组合逻辑实现的。后面我会详细拆解这块的落地方式。整套方案落地之后实际效果确实立竿见影弱网环境下单张图片上传成功率从原来的六七成提升到了九成以上图片数据能够自动分片上传到应用服务器再由服务端转存到云端对象存储因为有了分片索引传了一半断掉之后重新进入页面能够秒跳过已传的分片继续把剩下的传完。对于农田监控这种“设备无人值守、网络不可控”的场景这套机制算是比较稳的。这篇文章不打算只讲怎么用插件而是把整个方案的完整流程过一遍设计思路、分片与断点续传的细节、与云端同步的对接方式、以及我在实际项目中排过的一些坑。已经踩过一遍的人可以直接对照排查刚开始做的也能少走弯路。2. 整体设计思路把上传能力拆成“采集端、传输层、服务端、云端”四段2.1 农田监控场景下的技术需求拆解做农田监控图片上传绝对不是简单地把一个上传组件塞进页面就完了。图片从摄像头终端到云端中间要经过采集、传输、存储三个环节每个环节都有不同的约束条件。先说采集端。农田监控终端的图片不是用户手动选择上传的而是终端通过接口把图片文件拉取到浏览器端或者由前端从某个目录读取后触发上传。整个过程是自动化的不需要用户点击“选择文件”按钮。这就决定了WebUploader的picker控件只是一个辅助入口真正触发上传靠的是逻辑代码手动调用upload方法。然后是传输层。农田现场的带宽质量很差丢包率高延迟大。这种情况下直接把整张图片作为一个数据包发送失败成本太高。分片上传的意义就是把一个大任务拆成多个小任务每个分片独立传输、独立校验失败之后只需要重试失败的那一片而不是整个文件重来。服务端在这里的角色是临时接收和重组。每个分片到达服务端之后先落在临时目录全部到达后再按分片序号合并成完整文件。合并完成后服务端再把完整文件转存到云端对象存储。转存成功后需要给前端返回一个可靠的访问地址和状态标记。云端存储是最终落点。农田图片数据量不大单张从几百KB到几MB不等但数量多、持续性长日积月累之后存储量不小。放云端的好处是免运维、容量可扩展、后续做算法分析时云服务商提供接口直接读图也很方便。2.2 为什么分片大小定在2MB到5MB之间分片大小是传参时很核心的一个参数。WebUploader默认配置里分片大小是按字节算的chunkSize这个字段直接决定一个文件被切成多少片。我在项目里设置的是2MB一片。当时也试过1MB和5MB对比下来2MB在弱网环境下相对均衡。这个均衡体现在两个维度。分片太小比如1MB单片传输时间短失败重试成本低但分片数量会变大。一个10MB的文件就要切成10片每片都带独立请求头和校验信息整体请求次数增加服务端处理次数也增加而且分片数量越多并发控制越容易出问题队列里积压的分片等待时间变长。分片太大比如5MB以上虽然请求次数少但弱网环境下单片传输时间拉长一旦网络波动导致单片传输超时重试这一片的时间成本就很高。2MB在大多数2G3G现场网络环境下大约几秒内能完成一片传输重试代价也可接受。代码里配置分片大小大概长这样chunked: true, chunkSize: 2 * 1024 * 1024,这个配置加上之后WebUploader会在内部把文件切成多个Blob块每个块作为独立的请求体发送。2.3 前端组件封装直接放页面里会很难维护在Vue2项目里用WebUploader我强烈建议封装成一个独立的组件而不是在每个页面里重复初始化。原因很直接WebUploader的初始化依赖DOM节点组件卸载时需要手动销毁上传实例否则会留下定时器和内存引用。封装成组件可以在mounted里初始化在beforeDestroy里销毁生命周期清晰。组件内部需要维护的状态包括上传实例、上传进度百分比、当前文件状态、已上传分片列表。这些状态在组件模板中绑定显示用来驱动UI的进度条和状态文字。我习惯把WebUploader相关的初始化代码单独放到一个js文件里暴露创建实例的方法组件里调用这个方法获得上传实例。这样后续如果要从WebUploader迁移到其他上传方案改动可以集中在一个文件里不用翻组件代码。另外要注意WebUploader在Vue2的响应式系统里不是完全友好的。它的内部状态变动不会触发Vue的更新机制所以需要手动用this.$set或赋值新对象的方式来触发视图刷新。这一点在写进度条绑定的时候特别容易踩坑后面我会单独说明。3. 核心细节拆解分片、断点续传、并发与重试机制3.1 分片上传到底是怎么工作的简单解释一下WebUploader的分片逻辑。它在内部使用HTML5的File APIFile对象可以按字节范围切出Blob数据块。当chunked为true时上传器会把整个文件按照chunkSize大小依次切成多个Blob块然后把每个块单独打包发送。每个分片请求都会携带文件标识和分片序号信息。文件标识通常用文件的md5值来生成分片序号则是从0开始的整数。服务端根据文件标识创建临时目录把分片文件按序号存下来。这个机制的巧妙之处在于每个分片是独立的HTTP请求服务端收到哪个分片就保存哪个分片不依赖前一个分片的成功。这样即使第3片失败了第4片照样可以传成功前端只需要记录失败的分片序号等网络恢复后单独重发失败的那片。WebUploader本身会处理一部分重发逻辑它内部有一个失败队列上传失败的分片会自动进入重试流程。但默认的重试次数是有限的所以生产环境下还需要配合手动重试机制确保传了一半的图片继续传完。3.2 断点续传的核心md5标识和已传分片查询标题里提到的断点续传是这个项目的重头戏。要理解断点续传的原理先要理解一个前提同一张图片不管从哪个终端传它的文件内容是固定的。通过文件内容的md5值可以生成一个唯一的文件标识。这个标识不会因为上传中断而改变。具体做法是这样的第一步文件加入上传队列时先计算文件的md5。对于几MB的图片md5计算速度很快基本不影响用户体验。但如果是几十MB的大文件md5计算会阻塞主线程需要注意计算时机。WebUploader官方插件里提供了md5的接入方式可以在file对象加入队列后异步计算md5计算完成后赋值给file对象。第二步前端带着这个md5标识请求服务端一个查询接口问一下“这个文件你之前传过没有传了哪些分片”服务端根据md5找到临时目录扫描里面的分片文件返回一个已传分片序号列表。第三步前端把已传分片序号集合传给WebUploader让它在上传时跳过这些分片。WebUploader内部有一个skipAfterFile的逻辑大体实现是每个分片开始上传前先判断当前序号是否在“已传分片”集合中如果在就直接标记为“已上传”跳转到下个分片。这样可以做到一个很实际的效果一张5MB的图片分了3片传完了第1片和第2片网络断了。用户刷新页面重新进来再次拖入同一张图片系统计算出一样的md5查询到服务端已有第1片和第2片于是只传第3片。传完后端合并得到完整文件。用户感知不到底层经历了这么多步骤只看到进度条嗖一下就到了60%。3.3 并发上传与顺序合并之间的平衡WebUploader有一个并发控制机制threads参数控制同时上传的分片数。默认值是3也就是同时最多有3个分片请求在网络中传输。农田弱网场景下并发数太高不一定是好事。每个分片请求都会占用连接资源弱网环境下并发太多容易导致丢包率上升反而拖慢整体速度。我实测下来threads设置为2或3比较合适。设置成1太保守上传速度慢设置成5甚至更高在网络质量差的现场会出现大量请求超时需要在重试逻辑上花更多精力。并发上传最重要的一个约束是各分片可以乱序到达服务端但服务端合并文件时必须严格按分片序号排序不能按照到达时间排序。所以在服务端合并逻辑里必须先读取分片文件的文件名从中解析出序号然后按序号排序后顺序写入最终文件。前端传给服务端的请求参数里最核心的三个字段是chunk当前分片的序号从0开始chunks总分片数fileMd5整个文件的md5标识服务端可以完全依赖这三个字段完成分片接收和合并不需要前端额外传文件大小等字段。3.4 上传失败重试机制的设计WebUploader默认遇到分片上传失败时会自动重试默认重试次数是3次。但这个默认值在弱网环境下不够用我在项目里调整到了5次并且每次重试之间的间隔设置成递增的避免连续重试导致服务端压力过大。retry: 5,重试之外的另一个重要机制是手动恢复。页面里提供一个“继续上传”按钮点击后遍历上传队列中状态为error的文件重新调用upload()方法。这个动作本质上是把失败的分片重新加入上传队列。还有一点容易被忽略文件的md5计算和分片上传之间的时序关系。WebUploader中md5计算是异步的文件加入队列后上传动作可能会先于md5计算完成开始。这就会导致一个问题分片请求携带的fileMd5字段是空的。所以必须在确认md5计算完成之后再启动实际上传流程。我的做法是监听md5_complete事件在这个事件里调用uploader.upload()而不是在addFile事件里直接触发上传。4. 与云端的同步逻辑前端、应用服务器、对象存储三方如何配合4.1 常见方案对比谁来做“中转”这件事很多人的第一反应是前端直传云端对象存储。前端分片直接上传到对象存储的临时目录全部传完后对象存储自动合并或前端触发合并操作。这个方案技术上可行分片上传接口几个主流对象存储服务商都支持但实际在农田监控这个场景里不太适用。原因有两点。第一前端直传需要暴露云端临时密钥或者使用签名URL签名有效期和权限控制会比较复杂一旦密钥泄露存储桶安全就会出问题。第二监控图片后续要进入业务系统的分析链路服务端需要在图片落地后进行一系列处理比如写入数据库记录、触发算法分析、归并到对应的地块和摄像头维度。这些操作在直传方案里不好实现。所以我采用的是“前端分片到应用服务器应用服务器合并后转存云端”的方案。整个同步链路是这样走的前端WebUploader分片上传到应用服务器的接收入口应用服务器按md5标识归集分片文件所有分片到齐后应用服务器合并完整图片文件应用服务器调用云端对象存储的SDK把合并后的文件转存到云端存储桶转存成功后应用服务器把云端返回的文件URL、存储路径写入业务数据库前端通过查询接口获取图片的云端地址用于展示或后续分析4.2 前端在同步链路中需要做的事前端在整个同步链路中的职责不仅仅是“把文件传上去”。因为在服务端合并完成、转存云端的这段时间里图片对业务系统来说还不可用。所以前端需要轮询或等待服务端告知“图片已完成转存”然后刷新页面图片列表。我这里给出一个实际可用的处理方式前端在上传队列全部完成之后立即调用一个“文件状态查询”接口传入文件的md5标识接口返回该文件是否已合并完成、是否已同步到云端。如果状态是“同步中”前端每隔3秒轮询一次直到状态变为“已同步”或“同步失败”。这个轮询操作需要放在组件内部管理组件销毁时清除定时器避免组件卸载后定时器还在执行回调导致报错。表单参数里还可以附带业务字段比如地块编号、摄像头设备编号、拍摄时间。这些字段会随着分片上传请求一起到达服务端服务端在合并完成后写入业务表。这样图片和业务数据就能关联起来。4.3 服务端接口与参数约定整个方案里前后端接口约定越早明确开发越顺畅。这里给出我在项目里实际使用的一套接口约定几乎是这套方案的最小集。第一个接口是创建上传会话也可以叫注册文件POST /api/upload/register 参数fileMd5, fileName, fileSize, chunkSize, chunks 返回uploadId以及该文件已上传的分片序号列表前端在文件md5计算完成后调用这个接口拿到已传分片列表。第二个接口是分片上传接口POST /api/upload/chunk 参数uploadId, chunk, chunks, fileMd5, file, bizType, deviceId这里bizType可以标识图片类型比如“生长监测”、“虫情识别”deviceId用于关联摄像头设备。第三个接口是合并通知接口POST /api/upload/merge 参数uploadId, fileMd5前端所有分片上传完成后调用这个接口通知服务端开始合并。服务端根据uploadId找到该文件的所有分片按序号排序合并成完整文件然后转存云端存储。第四个接口是文件状态查询接口GET /api/upload/status?fileMd5xxx 返回statusuploading/merging/syncing/success/failedcloudUrl同步成功后的云端地址这一套接口的优点是职责划分清晰前端只需要专注于分片传输服务端专注于分片归集和合并转存业务数据和图片数据在服务端自然衔接。4.4 云端存储路径如何规划云端存储路径规划看起来是个小事但在农田监控数据量大了以后路径规划不合理会非常难受。我推荐的路径格式是bucket-name/{bizType}/{deviceId}/{yyyyMMdd}/{fileMd5}.jpg比如某个摄像头的生长监测图片存储路径就是agri-monitor/growth/cam-site-01/20250603/7f3a...x9.jpg这样的好处是按业务类型分类清晰调试的时候打开存储桶就能直观看到按设备维度隔离避免不同设备数据混杂按日期分目录后续做定时归档、冷热分层存储时可以直接按前缀匹配文件名用md5可以保证同一张图片不会重复存储节省存储成本。5. 实操实现Vue2组件里一步步落地这套流程5.1 初始化WebUploader实例先说页面结构。Vue2组件的模板里需要有一个承载上传按钮的DOM节点以及一个展示上传进度和状态信息的区域。template div div iduploaderContainer div idfilePicker选择图片/div /div div classprogress-wrapper span当前进度{{ progressPercent }}%/span span状态{{ uploadStatus }}/span /div /div /template然后在mounted里初始化上传实例。要注意WebUploader的容器节点必须在初始化时已经渲染完成所以需要放在this.$nextTick里执行。mounted() { this.$nextTick(() { this.initUploader(); }); },initUploader的完整初始化逻辑涉及几个关键配置上传地址指向服务端分片接收入口分片是否开启分片大小并发数文件接收校验以及几个事件监听。WebUploader的事件体系比较丰富我监听的事件集中在fileQueued、md5_complete、uploadProgress、uploadSuccess、uploadError、uploadComplete这几个上面。初始化代码示例initUploader() { const uploader WebUploader.create({ swf: /static/Uploader.swf, server: /api/upload/chunk, pick: { id: #filePicker, multiple: true }, accept: { title: Images, extensions: jpg,jpeg,png, mimeTypes: image/jpg,image/jpeg,image/png }, chunked: true, chunkSize: 2 * 1024 * 1024, threads: 2, retry: 5, auto: false, fileNumLimit: 50, fileSizeLimit: 50 * 1024 * 1024, formData: { bizType: this.bizType, deviceId: this.deviceId } }); this.uploader uploader; this.bindUploaderEvents(uploader); }这里有几个容易踩的坑。第一个是auto必须设置为false因为我们不希望在文件加入队列后立即上传而是要等md5计算完成、拿到已传分片列表之后再手动触发上传。第二个是server地址指向的是分片上传接口不是合并接口也不是普通文件上传接口。第三个是formData里可以携带通用业务参数这些参数会附加在每个分片请求上。5.2 md5计算与断点续传的衔接WebUploader官方示例中md5计算是通过插件方式引入的。我这里使用的是WebUploader的md5插件配合md5_complete事件。bindUploaderEvents(uploader) { uploader.on(fileQueued, (file) { this.uploadStatus 正在计算文件指纹; }); uploader.on(md5_complete, async (file, md5) { file.fileMd5 md5; const res await this.checkUploadStatus(md5); this.uploadedChunks res.uploadedChunks || []; this.uploadId res.uploadId || ; this.uploadStatus 准备上传; uploader.upload(); }); }这里需要特别说明一下checkUploadStatus的实现。这个函数调用服务端接口传入md5拿到已传分片列表。WebUploader本身不会自动跳过已传分片需要在每个分片发出前做拦截判断。WebUploader提供了uploadBeforeSend事件返回false可以阻止分片上传。在这个事件里可以判断当前分片是否已存在uploader.on(uploadBeforeSend, (file, data) { if (file.fileMd5) { data.fileMd5 file.fileMd5; } if (this.uploadedChunks.includes(data.chunk)) { return false; } });当某个分片序号在已传分片列表里时返回falseWebUploader会跳过这个分片不发起网络请求。这样断点续传的逻辑就闭环了。另外还要提到uploadAccept事件。这个事件可以自定义判断服务端返回结果是否成功。比如服务端返回的JSON中code为200表示分片接收成功如果code表示“分片已存在”也可以视为成功不需要重传。这样可以避免服务端已存在分片但前端不知道导致的无谓重传。5.3 进度反馈与Vue响应式更新WebUploader的uploadProgress事件会返回file对象和当前上传百分比但这个百分比是整个文件的整体进度。对于分片上传来说这个事件在不同分片完成时会跳变视觉上可能不够平滑。我处理进度的方式是自己计算进度。记录已完成的字节数除以文件总字节数。uploader.on(uploadProgress, (file, percentage) { const percent Math.round(percentage * 100); this.progressPercent percent; });这里有一个Vue2的响应式细节WebUploader的回调不在Vue的响应式上下文中直接给this.progressPercent赋值是可以触发更新的因为progressPercent是组件data里声明过的属性。如果是给data中某个对象动态添加新属性才需要用$set。实际项目中我还会展示当前上传的分片序号方便排查问题uploader.on(uploadChunkProgress, (file, chunk, e) { this.currentChunk chunk; });uploadChunkProgress事件是WebUploader提供的分片级进度事件可以拿到当前正在上传的分片序号。调试弱网问题时这个信息非常有用能直观看到卡在哪个分片上。5.4 组件销毁清理WebUploader实例在创建后会启动定时轮询任务和DOM事件绑定。如果组件销毁时不清理会和页面上其他组件产生残留引用严重时导致页面卡顿或内存泄漏。beforeDestroy() { if (this.uploader) { this.uploader.destroy(); this.uploader null; } if (this.pollTimer) { clearInterval(this.pollTimer); this.pollTimer null; } }这个清理动作在Vue2项目中特别重要。我在一个页面里反复切换摄像头设备时曾遇到过上传队列残留的问题。后来排查发现就是旧的上传实例没有被销毁新旧实例的事件都在触发。销毁逻辑补上之后问题就消失了。6. 遇到的坑与排查思路6.1 分片错乱导致合并后的图片损坏症状前端显示上传成功服务端也显示合并完成但打开图片显示文件损坏无法预览。排查过程先看服务端日志里分片接收情况发现接收到的分片数量没问题但大小不一致有几个分片文件明显小于2MB。进一步看请求日志发现这些小的分片文件对应的是重试请求重试时上传的字节数和首次上传时不一致。根因前端WebUploader在重试分片时由于网络原因导致请求被服务端提前截断服务端保存了不完整的分片文件。但前端认为这个分片已经发送成功后续不会重传。解决办法服务端在保存分片时校验文件大小如果分片大小和预期不符返回错误状态码。前端收到错误码后重新上传该分片。同时在合并之前服务端校验所有分片大小是否与chunks和chunkSize匹配不匹配则返回合并失败。6.2 页面刷新后断点续传失效症状传了一半的图片刷新页面再传进度条从0开始重新上传。排查过程检查md5计算逻辑发现文件加入队列后md5_complete事件可能没有触发。进一步检查发现WebUploader默认只有在新文件加入队列时才计算md5而刷新后文件是重新加入队列的所以理论上应该重新计算。问题出在md5计算完成前页面已经调用了uploader.destroy()导致计算任务被中断。解决办法在组件销毁时不立即销毁上传实例而是等md5计算完成后再销毁。或者更简单一点刷新页面后重新初始化上传器不要依赖同一实例跨页面保留状态。另外断点续传还依赖一个前提前端必须知道已传分片列表。如果服务端临时文件被清理策略清掉了前端拿到的已传分片列表是空的就只能重新上传。生产环境要给服务端临时目录设置合理的保留时间我通常保留24小时。6.3 uploadBeforeSend事件里访问不到业务参数症状分片请求发出后服务端日志里没有收到bizType参数。排查过程配置里已经通过formData传了bizType但未生效。后来查阅WebUploader源码发现formData里的参数会作为初始参数提交但分片上传请求的最终参数是uploadBeforeSend返回值里的data对象。如果在uploadBeforeSend里返回了新的data对象原formData参数就会丢失。解决办法在uploadBeforeSend里手动合并参数uploader.on(uploadBeforeSend, (file, data) { data.fileMd5 file.fileMd5; data.bizType this.bizType; data.deviceId this.deviceId; data.uploadId this.uploadId; });这样确保每个分片请求都携带完整的业务参数。这个坑比较隐蔽分享出来希望后面的人少花点排查时间。6.4 服务端临时文件一直累积现象部署运行一段时间后服务器磁盘占用不断上升检查发现临时目录里堆了大量分片文件。原因很多分片上传到一半中断前端没有再重试服务端临时文件也没有清理机制。解决办法在服务端加一个定时清理任务每6小时扫描一次临时目录删除创建时间超过24小时且尚未合并的临时分片文件。合并成功的分片文件在合并完成后立即删除。这样磁盘占用能保持在一个稳定水平。6.5 上传并发数设置不当导致整体变慢现象现场网络质量差并发上传时多个分片请求同时超时整体速度反而比单分片上传还慢。原因分析弱网环境的带宽有限并发数设置太高会让每个分片分到的带宽变少任何一个分片都不容易完成传输。而且2G网络下TCP连接并发能力有限大量并发会导致连接互相挤占。解决方式根据现场网络情况动态调整并发数。我做过一个简单策略页面加载后先上传一个500KB的测试分片记录耗时如果超过5秒就把并发数动态调低到1如果耗时在2秒以内保持默认的2或3。这个调整逻辑在项目里实测效果还是比较明显的。7. 云端同步的几个关键细节7.1 转存云端失败的兜底策略分片上传到应用服务器并合并成完整文件之后还要面临一个潜在问题转存云端失败。可能是网络波动也可能是云端临时故障。如果合并完就直接丢给云存储服务商失败后前端已经显示上传成功但实际图片没有进入云端业务数据里就会缺少图片。我的解决方案是在服务端做一个简单的重试机制合并完成后先记录本地文件路径再调用云端SDK转存。转存失败时先不删除本地文件将任务标记为待重试状态由一个后台定时任务每隔5分钟重试一次最多重试3次。超过3次仍然失败则标记该文件同步失败同时记录日志方便后续人工介入。前端查询文件状态的接口里增加一个“确认图片可用”的逻辑。状态为“已同步”时前端展示图片状态为“同步失败”时前端显示失败占位并允许用户重新触发一次同步操作。7.2 同名文件的云端覆盖策略农田监控图片虽然是按md5命名但会出现这样一种情况同一时刻不同摄像头拍摄的内容不同但图片编码压缩后md5完全相同比如纯色背景的图片。如果直接用md5作为云端文件名会出现两张不同业务含义的图片被同一个文件名覆盖的问题。解决方法是云端存储路径里加入设备维度agri-monitor/growth/cam-site-01/20250603/{md5}.jpg对于同一台设备、同一类型、同一天的图片md5相同的概率极低。即使出现了业务层面也基本可以视为重复数据覆盖是合理的。如果业务上要求保留每次拍摄的独立记录可以在md5后面追加时间戳或序号.../20250603/{md5}_{timestamp}.jpg这样既避免了覆盖又保留了去重能力通过md5前缀可以判断是否为同一图片。7.3 云同步完成后的回调通知服务端转存云端成功后除了写入数据库还要触发一个回调通知前端“图片已经可以用了”。这个回调我实现为前端在合并完成后调用状态查询接口服务端返回同步状态。前端轮询直到状态变为成功。具体轮询逻辑如下async checkSyncStatus(fileMd5) { const res await queryFileStatus(fileMd5); if (res.status success) { this.cloudUrl res.cloudUrl; this.uploadStatus 已完成云端同步; clearInterval(this.pollTimer); } else if (res.status failed) { this.uploadStatus 同步失败; clearInterval(this.pollTimer); } else { this.pollTimer setTimeout(() { this.checkSyncStatus(fileMd5); }, 3000); } }这套轮询方案虽然简单但很实用做起来也直观。8. 生产环境下的几个额外注意点8.1 WebUploader与Vue2的兼容性问题Vue2的响应式系统通过Object.defineProperty劫持对象属性而WebUploader内部维护了大量私有属性。直接把WebUploader实例挂到Vue组件的data里Vue会尝试对实例进行响应式劫持这可能导致两个问题一是性能下降二是WebUploader内部属性被改写引发异常。建议的做法是把uploader实例作为组件的普通属性挂载不要放进data。也就是在this上直接定义this.uploader uploader;而不是this.$data.uploader uploader;这个区别看似小但实际影响很大。前者仅仅是给组件实例添加属性后者会触发响应式转换。8.2 文件上传前的大小和时间过滤农田监控终端可能会产生大量图片但并不是所有图片都需要上传。前端在文件加入队列后增加过滤条件可以节省大量流量。我处理的过滤逻辑包括图片大小超过20MB的直接拒绝拍摄时间超过当前时间5分钟的图片标记为异常数据只记录不上传同一摄像头一分钟内重复图片自动去重md5首次出现时上传后续相同md5的文件直接忽略。这些过滤条件放在fileQueued事件里处理符合条件的才允许进入上传队列。这样能减少无效文件的传输在弱网环境里省下的流量就是省下的时间。8.3 监控终端长时间掉线后的恢复上传农田监控设备可能长时间离线重新上线后会积攒一段时间的图片。如果这些图片全部进入同一个上传队列会导致队列过长、排队等待时间太久。我的做法是把上传队列拆成多个批次。每批最多传10张图片传完一批再进下一批。批次之间的间隔时间也做了控制避免集中上传给本就不好的网络造成冲击。uploader.on(uploadFinished, () { if (this.pendingFiles.length 0) { const nextBatch this.pendingFiles.splice(0, 10); uploader.addFiles(nextBatch); uploader.upload(); } });这个分批策略在实际部署中效果很明显设备恢复上线后图片上传的成功率保持了稳定。8.4 前端图片压缩与格式统一农田监控的原始图片有时会非常大特别是高分辨率摄像头拍出来的照片单张可能达到十几MB。在弱网环境下直接传原图不现实。我在前端接入了一个图片压缩环节。利用canvas对图片进行等比压缩控制最终上传的图片宽度不超过1280像素质量按0.8压缩。压缩后图片大小通常在200KB到500KB之间上传速度快了很多云端存储成本也低了。要注意的是压缩操作会改变图片的md5值。所以在计算md5时要等压缩完成后再计算否则断点续传的标识就不准了。9. 写在最后的一些经验心得这套方案从最初搭建到最终稳定运行前后经历了几个版本的迭代。回过头看最核心的取舍就是不在前端做太复杂的业务判断把分片、重试、断点这些能力交给WebUploader把业务逻辑放在服务端和云端的衔接处。WebUploader虽然是个老牌组件维护状态不如新项目活跃但它在分片传输这个细分场景里沉淀了很多成熟的设计。在Vue2项目里集成它只要把几个关键点处理好稳定性是有保障的。这里再整理几条心得供参考第一分片大小不要照抄网上文章最好在实际网络环境里测试几组参数再定。我这边2MB是最优值换个网络环境可能就不一样。第二断点续传的实现前提是前后端对md5和分片序号的约定完全一致接口文档里必须明确字段含义避免出现前端用的是数字序号、服务端按字符串排序的尴尬情况。第三云端同步是否成功一定要有明确的状态反馈给前端。宁可多做一次轮询也不要让用户看着“上传成功”但图片实际没有进云端。第四任何组件都有生命周期Vue组件销毁时彻底清理资源是基本功但WebUploader的销毁处理尤其重要这一点在页面上反复切换场景时感受最深。第五生产环境的日志一定要把分片上传的关键参数打全fileMd5、chunk、chunks、文件大小、耗时、结果状态。排查问题时这些日志比经验判断靠谱得多。这套“Vue2 WebUploader 服务端分片归集 云端同步”的方案适合的场景是弱网环境下的大文件可靠上传。不只是农田监控图片类似的设备巡检照片、现场施工图片回传、离线数据包上报都可以参考这个架构来实现。把分片做好把断点续传做好把云端同步的链路打通后续业务只需要关注自己的业务逻辑就行了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑