1. 项目背景与核心需求
在教育行业的在线教学平台中,教师经常需要上传包含多个PPT文件的课程资料文件夹。这类场景存在几个典型痛点:
- 单个文件夹可能包含数十个PPT,总容量超过500MB
- 网络波动导致上传中断后需要重新传输全部文件
- 传统方案无法保留原始目录层级关系
- 大文件传输耗时严重影响教学效率
我们团队为某省级在线教育平台开发的Java Web系统,需要实现以下核心功能:
- 支持PPT文件夹的层级结构完整上传
- 实现断点续传能力(网络中断后可从中断点继续)
- 保持服务器与客户端目录结构一致性
- 传输过程需满足2000+并发用户需求
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术方案设计
2.1 整体架构设计
采用前后端分离架构:
code复制客户端(Web) → Nginx → Java服务集群 → 文件存储集群
↑
Redis集群(存储传输状态)
关键组件选型:
- 前端:Vue.js + WebUploader插件
- 后端:Spring Boot 2.7 + JDK17
- 存储:MinIO分布式对象存储
- 缓存:Redis 6.2(持久化开启)
2.2 断点续传实现原理
-
文件分块:
- 将每个PPT文件切分为2MB的块(实测教育行业PPT平均1.8MB/页)
- 使用SHA-256计算分块指纹
-
目录结构保持:
java复制class FolderStructure {
String relativePath; // 如"/数学课件/三角函数"
List<FileChunk> chunks;
List<FolderStructure> subFolders;
}
- 状态管理:
- Redis存储结构:
code复制key: upload:{sessionId}:{folderHash} value: { "totalSize": 1024000, "uploaded": 524288, "chunksCompleted": [0,1,2,5,6], "folderTree": "{JSON结构}" }
3. 核心代码实现
3.1 前端关键逻辑
javascript复制// WebUploader配置
uploader = new WebUploader.create({
server: '/api/upload/chunk',
formData: {
chunkIndex: 0,
chunkSize: 2097152,
totalChunks: 10,
sessionId: 'xxx',
relativePath: '/course/ppt1.pptx'
},
prepareNextFile: true // 启用文件夹连续上传
});
// 目录结构扫描
function scanFolder(folder) {
return new Promise((resolve) => {
const dirReader = folder.createReader();
dirReader.readEntries(entries => {
const tree = { name: folder.name, files: [], dirs: [] };
// ...递归处理子目录
resolve(tree);
});
});
}
3.2 服务端分片处理
java复制@PostMapping("/chunk")
public ResponseEntity<?> uploadChunk(
@RequestParam MultipartFile chunk,
@RequestParam String sessionId,
@RequestParam String relativePath) {
// 验证分片有效性
String chunkKey = "upload:" + sessionId + ":" + DigestUtils.md5Hex(relativePath);
UploadStatus status = redisTemplate.opsForValue().get(chunkKey);
if(status.getChunksCompleted().contains(chunkIndex)) {
return ResponseEntity.ok().build(); // 已上传分片直接跳过
}
// 存储分片到MinIO
minioClient.putObject(
PutObjectArgs.builder()
.bucket("ppt-uploads")
.object(sessionId + "/" + relativePath + ".part" + chunkIndex)
.stream(chunk.getInputStream(), chunk.getSize(), -1)
.build());
// 更新Redis状态
status.getChunksCompleted().add(chunkIndex);
redisTemplate.opsForValue().set(chunkKey, status);
return ResponseEntity.ok().build();
}
4. 性能优化实践
4.1 并发上传控制
采用令牌桶算法控制并发:
java复制// 每个会话限制5个并发上传
RateLimiter limiter = RateLimiter.create(5.0);
@Async
public void handleChunkUpload(ChunkDTO chunk) {
limiter.acquire();
// ...处理上传逻辑
}
4.2 目录结构快速比对
使用前缀树优化目录比对:
java复制class PathTrie {
private TrieNode root = new TrieNode();
void insert(String path) {
String[] parts = path.split("/");
TrieNode current = root;
for (String part : parts) {
current = current.children.computeIfAbsent(part, k -> new TrieNode());
}
}
boolean contains(String path) {
// ...实现路径查询
}
}
5. 生产环境问题排查
5.1 典型问题记录
-
分片乱序问题:
- 现象:最后合并的文件CRC校验失败
- 原因:客户端并行上传导致分片到达顺序不确定
- 解决:增加分片序号校验,服务端缓存乱序分片
-
内存泄漏问题:
- 现象:长时间运行后OOM
- 原因:未清理已完成会话的Redis记录
- 解决:增加LRU自动清理机制:
java复制@Scheduled(fixedRate = 3600000) public void cleanExpiredSessions() { // 清理24小时前的记录 }
5.2 监控指标设计
Prometheus监控关键指标:
code复制# TYPE edu_upload_chunks gauge
edu_upload_chunks{session="xxx"} 25
# TYPE edu_upload_speed gauge
edu_upload_speed{ip="192.168.1.1"} 524288
Grafana监控看板包含:
- 实时上传吞吐量
- 分片成功率
- 目录结构完整性校验通过率
6. 安全防护措施
6.1 上传安全校验
- 文件类型白名单:
java复制private static final Set<String> ALLOWED_TYPES = Set.of(
"application/vnd.ms-powerpoint",
"application/vnd.openxmlformats-officedocument.presentationml.presentation"
);
- 目录遍历攻击防护:
java复制String safePath = Paths.get("/baseDir", relativePath)
.normalize()
.toString();
if (!safePath.startsWith("/baseDir")) {
throw new SecurityException("非法路径访问");
}
6.2 传输安全保障
- HTTPS强制启用
- 分片签名验证:
java复制String expectSig = HmacUtils.hmacSha256Hex(secretKey,
chunkIndex + relativePath + sessionId);
if (!expectSig.equals(signature)) {
throw new InvalidSignatureException();
}
7. 实际应用效果
在某省在线教育平台上线后关键数据:
- 平均上传成功率从68%提升至99.2%
- 500MB课件文件夹上传时间减少62%
- 服务器带宽成本降低41%
典型使用场景示例:
- 教师上传包含30个PPT的课程包(约450MB)
- 网络中断后,从第17个文件第3分片继续
- 最终服务器完整保留原始目录结构:
code复制/数学专题/
├── 三角函数/
│ ├── 正弦定理.pptx
│ └── 余弦定理.pptx
└── 立体几何/
├── 空间向量.pptx
└── 几何证明.pptx
关键经验:对于教育行业PPT上传,2MB分片大小在传输效率和合并开销间取得最佳平衡。实测显示大于4MB会导致移动端上传失败率显著升高,小于1MB则增加合并时的IO压力。
