1. 项目背景与核心挑战
在能源化工行业的生产现场,管道巡检是保障安全生产的重要环节。巡检人员每天需要记录大量包含高清照片、视频和传感器数据的日志附件,单个文件经常达到数百MB甚至GB级别。传统的文件上传方案在面对这种场景时存在三个致命问题:
- 网络不稳定导致传输中断后需要重新上传
- 大文件传输占用带宽影响其他业务系统
- 服务器内存溢出风险(特别是并发上传时)
我们团队为某大型石化企业实施的解决方案,通过Java实现了支持TB级附件上传的断点续传系统。这套系统上线后使巡检日志上传成功率从78%提升至99.9%,平均传输时间缩短60%。下面分享具体实现方案。
2. 技术架构设计
2.1 整体架构
采用分层设计保证系统扩展性:
code复制[移动端APP] --HTTPS--> [API网关] --内网-->
[文件分片服务] --消息队列--> [存储集群]
关键组件说明:
- 移动端:集成Android/iOS SDK处理文件分片
- 网关层:Spring Cloud Gateway实现鉴权和流量控制
- 分片服务:Spring Boot + Netty处理文件块
- 存储层:MinIO集群存储原始文件
2.2 断点续传流程
-
初始化上传:
- 客户端计算文件MD5和分片信息(每片5MB)
- 服务端创建上传任务记录
-
分片传输:
- 采用HTTP Range头实现断点续传
- 失败分片自动重试3次
-
完整性校验:
- 服务端合并分片后验证MD5
- 异常分片触发补偿机制
3. 核心代码实现
3.1 分片算法
java复制// 根据网络质量动态调整分片大小
public int calculateChunkSize(NetworkQuality quality) {
switch(quality) {
case POOR: return 1 * 1024 * 1024; // 1MB
case FAIR: return 3 * 1024 * 1024; // 3MB
case GOOD: return 5 * 1024 * 1024; // 5MB
default: return 2 * 1024 * 1024;
}
}
3.2 服务端校验逻辑
java复制// 使用Redis记录上传进度
public boolean checkChunkExists(String fileMd5, int chunkIndex) {
String key = "upload:" + fileMd5;
return redisTemplate.opsForValue().getBit(key, chunkIndex);
}
4. 性能优化要点
4.1 内存控制
- 采用Zero Copy技术避免内存拷贝
- 限制并发上传线程数(建议CPU核心数×2)
- 使用DirectByteBuffer分配堆外内存
4.2 异常处理机制
- 网络抖动:指数退避重试策略
- 服务重启:通过checkpoint文件恢复进度
- 存储故障:自动切换到备用集群
5. 生产环境实测数据
在日均10万+附件的生产环境中:
| 指标 | 优化前 | 优化后 |
|---|---|---|
| 平均上传时间 | 8.7min | 3.2min |
| 失败率 | 22% | 0.1% |
| CPU占用峰值 | 85% | 45% |
6. 踩坑经验
-
MD5碰撞问题:
- 发现不同文件可能产生相同MD5
- 解决方案:追加文件大小作为二次校验
-
安卓端内存泄漏:
- 频繁创建ByteBuffer导致OOM
- 修复方案:使用对象池复用缓冲区
-
分钟级网络中断:
- 移动网络切换导致TCP连接僵死
- 增加心跳检测机制,超时自动重建连接
这套系统经过两年生产验证,稳定支持了该企业全球30多个生产基地的巡检作业。关键点在于:分片策略要动态可调、进度记录必须持久化、异常处理要考虑各种边界情况。