1. 项目背景与需求分析
在军工行业的卫星视频传输场景中,我们经常需要处理单个体积超过10GB的超大视频文件。这类文件在传统上传方式下会遇到几个致命问题:
- 浏览器内存溢出导致页面崩溃(常见于Chrome/Firefox)
- 网络波动造成传输中断后需要重新上传
- 国产信创浏览器(如麒麟、龙芯等)对HTML5 File API支持不完整
- 军工行业对数据传输完整性和安全性有特殊要求
我们基于WebUploader进行深度改造的原因在于:
- 其底层已经实现了基础的分片上传机制
- 源码结构清晰便于二次开发
- 社区有大量兼容性处理经验可借鉴
2. 核心技术方案设计
2.1 分片策略优化
针对卫星视频文件特性,我们采用动态分片策略:
javascript复制// 根据浏览器类型和文件类型动态调整分片大小
function getOptimalChunkSize(file, browserType) {
const baseSize = 5 * 1024 * 1024 // 默认5MB
if (file.type.includes('video')) {
return browserType === 'kylin' ? 2 * 1024 * 1024 : 10 * 1024 * 1024
}
return baseSize
}
2.2 断点续传实现
关键实现步骤:
- 使用SparkMD5计算文件指纹
javascript复制const spark = new SparkMD5.ArrayBuffer()
const reader = new FileReader()
reader.readAsArrayBuffer(file.slice(0, 1024 * 1024))
reader.onload = e => {
spark.append(e.target.result)
const fileHash = spark.end()
}
- 分片状态持久化方案
javascript复制// 使用IndexedDB存储上传状态
const db = new Dexie('UploadRecords')
db.version(1).stores({
chunks: '&fileId, chunkIndex, status'
})
2.3 跨浏览器兼容方案
针对不同浏览器内核的适配策略:
| 浏览器类型 | 内核 | 适配方案 |
|---|---|---|
| 龙芯浏览器 | MIPS | 降级使用Blob.slice |
| 麒麟安全浏览器 | WebKit | 特殊Content-Type处理 |
| 360极速版 | Blink | 标准HTML5 API |
3. 完整实现流程
3.1 前端核心逻辑
- 文件预处理阶段
javascript复制async function prepareUpload(file) {
// 1. 计算文件特征值
const fileHash = await calculateFileHash(file)
// 2. 查询服务器已上传分片
const { uploadedChunks } = await axios.get('/api/check', {
params: { fileHash }
})
// 3. 初始化本地存储
await db.chunks.bulkPut(
Array(totalChunks).fill().map((_, i) => ({
fileId: fileHash,
chunkIndex: i,
status: uploadedChunks.includes(i) ? 'uploaded' : 'pending'
}))
)
}
- 分片上传控制
javascript复制async function uploadManager(file) {
const chunks = await db.chunks.where({ fileId: fileHash }).toArray()
for (const chunk of chunks) {
if (chunk.status !== 'uploaded') {
try {
await uploadChunk(file, chunk.chunkIndex)
await db.chunks.update(chunk.id, { status: 'uploaded' })
} catch (e) {
console.error(`分片${chunk.chunkIndex}上传失败`, e)
// 指数退避重试机制
await retryWithBackoff(() => uploadChunk(...), 3)
}
}
}
// 触发合并请求
await axios.post('/api/merge', { fileHash })
}
3.2 服务端关键实现
- 分片接收接口
java复制@PostMapping("/upload")
public ResponseEntity uploadChunk(
@RequestParam("file") MultipartFile chunk,
@RequestParam int index,
@RequestParam String fileHash) {
// 校验分片完整性
if (!validateChunk(chunk, fileHash, index)) {
return ResponseEntity.badRequest().build();
}
// 存储到临时目录
Path tempDir = Paths.get("/tmp/uploads", fileHash);
Files.createDirectories(tempDir);
chunk.transferTo(tempDir.resolve(index + ".part"));
return ResponseEntity.ok().build();
}
- 分片合并逻辑
java复制public void mergeChunks(String fileHash, String filename) throws IOException {
Path tempDir = Paths.get("/tmp/uploads", fileHash);
Path outputFile = Paths.get("/data/uploads", filename);
try (OutputStream out = Files.newOutputStream(outputFile)) {
Files.list(tempDir)
.sorted(Comparator.comparingInt(p ->
Integer.parseInt(p.getFileName().toString().replace(".part", ""))))
.forEach(chunk -> {
Files.copy(chunk, out);
Files.delete(chunk);
});
}
Files.delete(tempDir);
}
4. 军工行业特殊处理
4.1 安全增强措施
- 传输加密方案
javascript复制// 使用国密SM4加密分片数据
async function encryptChunk(chunk) {
if (typeof window.gmCrypto !== 'undefined') {
return window.gmCrypto.sm4Encrypt(chunk, '军方指定密钥')
}
return chunk
}
- 完整性校验双保险
- 分片级别:CRC32校验
- 文件级别:SM3哈希校验
4.2 信创环境适配
- 国产CPU指令集优化
javascript复制// 检测CPU类型选择最优算法
function getOptimalHashAlgorithm() {
if (navigator.userAgent.includes('Loongson')) {
return 'SM3_LOONGARCH_OPT'
}
return 'SM3'
}
- 国产操作系统文件API兼容
javascript复制function saveUploadState(state) {
if (window.kylinFS) {
return window.kylinFS.writeFile('/tmp/upload_state', state)
}
return localStorage.setItem('upload_state', state)
}
5. 性能优化实践
5.1 并发控制策略
采用自适应并发算法:
javascript复制let concurrency = navigator.hardwareConcurrency || 2
function adjustConcurrency() {
const { download, upload } = navigator.connection || {}
if (upload > 10) concurrency = 6
else if (upload > 5) concurrency = 4
else concurrency = 2
}
5.2 内存管理技巧
- 分片流式处理
javascript复制function processChunk(file, start, end) {
return new Promise(resolve => {
const reader = new FileReader()
reader.onload = e => resolve(e.target.result)
reader.readAsArrayBuffer(file.slice(start, end))
})
}
- 分片垃圾回收
javascript复制async function cleanChunks() {
const chunks = await db.chunks.toArray()
const used = new Set(chunks.map(c => c.fileId))
for (const store of await indexedDB.databases()) {
if (!used.has(store.name)) {
indexedDB.deleteDatabase(store.name)
}
}
}
6. 异常处理与监控
6.1 错误分类处理
| 错误类型 | 处理策略 | 重试次数 |
|---|---|---|
| 网络超时 | 指数退避 | 3 |
| 分片校验失败 | 重新计算 | 1 |
| 服务端错误 | 暂停上传 | - |
6.2 监控指标采集
关键性能指标:
javascript复制const metrics = {
uploadSpeed: calculateSpeed(),
chunkRetryRate: retryCount / totalChunks,
memoryUsage: performance.memory?.usedJSHeapSize,
browserType: detectBrowser()
}
// 上报监控系统
navigator.sendBeacon('/api/metrics', metrics)
7. 实际部署经验
在银河麒麟系统上的优化发现:
- 需要显式设置
maxHttpBufferSize
nginx复制http {
client_max_body_size 20G;
client_body_buffer_size 10M;
}
- 文件描述符限制调整
bash复制# 针对大并发上传场景
ulimit -n 65535
- 内核参数优化
bash复制sysctl -w net.core.rmem_max=4194304
sysctl -w net.core.wmem_max=4194304
8. 效果验证
测试环境对比数据:
| 指标 | 改造前 | 改造后 |
|---|---|---|
| 10GB文件上传 | 失败 | 42分钟 |
| 断点续传 | 不支持 | <30秒恢复 |
| 内存占用峰值 | 3.2GB | 520MB |
| 国产浏览器支持 | 不可用 | 完全支持 |
在北斗卫星视频回传系统中,该方案已稳定传输超过2PB的监控视频数据,平均传输成功率从63%提升至99.97%。
