弱网下视频分片上传:断点续传、排队上传、失败重试、云端溯源绑定
弱网下视频分片上传断点续传、排队上传、失败重试、云端溯源绑定大家好我是黒漂技术佬。视频专题第二篇也是系列的倒数第二篇咱们聊视频上传。录好的视频存在本地没用出问题的时候得能调出来看。但4G弱网环境下传一个几MB的视频都可能断好几次传一半断了重传又浪费流量。今天给大家一套工业级的视频上传方案断点续传、排队调度、优先级管理、云端绑定全套都有。一、为什么视频上传是个难题先说说4G环境下传视频的痛点上传的几大难题网络不稳定传一半断了重传又得从头来浪费流量带宽有限4G上传带宽本来就小视频传起来慢不能影响业务上传占满带宽开门指令延迟了就麻烦了流量成本视频文件不小传多了流量费扛不住要能溯源传上去的视频得能跟订单、事件对应上核心目标稳定传上去不影响业务尽量省流量传完能对应上。二、分片上传与断点续传这是大文件上传的标准方案视频上传必须上。什么是分片上传把一个大视频文件切成一小块一小块的一块一块传原始视频video.mp4 (8MB) ↓ 切片 分片10 ~ 1MB 分片21MB ~ 2MB 分片32MB ~ 3MB ... 分片87MB ~ 8MB ↓ 逐个上传 全部传完后服务端合并成完整文件分片大小怎么选分片大小优点缺点适用场景256KB失败重传损失小请求次数多开销大网络很差的场景512KB均衡-推荐大部分场景1MB请求少效率高失败损失大网络较好的场景售货柜推荐512KB一片兼顾效率和容错。断点续传原理传了一半断了下次接着传不从头来上传前先问服务端这个文件传了多少了服务端返回已上传的分片列表客户端跳过已经传完的从下一片开始传全部传完通知服务端合并客户端实现publicclassChunkUploader{privatestaticfinalintCHUNK_SIZE512*1024;// 512KB一片/** * 上传视频文件 */publicbooleanuploadVideo(StringfilePath,StringfileId){FilefilenewFile(filePath);longfileSizefile.length();Stringmd5FileUtils.getFileMD5(file);// 1. 先查询已上传的分片ListIntegeruploadedChunksqueryUploadedChunks(fileId,md5);// 2. 计算总分片数inttotalChunks(int)Math.ceil((double)fileSize/CHUNK_SIZE);// 3. 逐个上传未完成的分片for(inti0;itotalChunks;i){if(uploadedChunks.contains(i)){continue;// 已经传过了跳过}booleansuccessuploadChunk(file,i,fileId,md5,totalChunks);if(!success){// 这一片传失败了下次重试Log.w(Upload,分片 i 上传失败);returnfalse;}// 每传完一片更新进度updateUploadProgress(fileId,i1,totalChunks);}// 4. 全部传完通知服务端合并returncompleteUpload(fileId,md5,fileSize);}/** * 上传单个分片 */privatebooleanuploadChunk(Filefile,intchunkIndex,StringfileId,Stringmd5,inttotalChunks){// 读取分片数据byte[]chunkDatareadChunk(file,chunkIndex,CHUNK_SIZE);// 构造请求MultipartBody.BuilderbuildernewMultipartBody.Builder().setType(MultipartBody.FORM).addFormDataPart(file_id,fileId).addFormDataPart(chunk_index,String.valueOf(chunkIndex)).addFormDataPart(total_chunks,String.valueOf(totalChunks)).addFormDataPart(file_md5,md5).addFormDataPart(file,chunk_chunkIndex,RequestBody.create(MediaType.parse(video/mp4),chunkData));// 发送请求带超时RequestrequestnewRequest.Builder().url(UPLOAD_URL).post(builder.build()).build();try{Responseresponseclient.newCall(request).execute();returnresponse.isSuccessful();}catch(IOExceptione){returnfalse;}}}MD5校验每个分片和整个文件都要校验防止传错了分片级每片传完校验MD5不对就重传这一片文件级全部传完校验整个文件的MD5确保完整三、上传队列与优先级调度不是所有视频都一样急分优先级排队传。优先级划分优先级视频类型上传时机说明最高异常事件视频立即上传异常开门、告警、人工调取高投诉/纠纷订单视频优先上传用户投诉了后台申请调取中正常开门视频闲时上传普通交易不急低巡检/测试视频有剩余流量再传可有可无的上传队列实现publicclassUploadQueueManager{privatePriorityQueueUploadTaskqueuenewPriorityQueue((a,b)-Integer.compare(b.getPriority(),a.getPriority()));privateUploadTaskcurrentTask;privatebooleanisRunningfalse;/** * 添加上传任务 */publicvoidaddTask(UploadTasktask){// 去重同一个文件不要重复加if(isInQueue(task.getFileId())){return;}queue.offer(task);if(!isRunning){startNextTask();}}/** * 开始下一个任务 */privatesynchronizedvoidstartNextTask(){if(queue.isEmpty()){isRunningfalse;return;}// 先检查网络弱网下不传低优先级的if(!canUploadNow()){isRunningfalse;return;}currentTaskqueue.poll();isRunningtrue;// 异步上传newThread(()-{booleansuccesschunkUploader.uploadVideo(currentTask.getFilePath(),currentTask.getFileId());onTaskFinished(currentTask,success);currentTasknull;startNextTask();}).start();}}网络条件判断弱网的时候不传或者只传高优先级的privatebooleancanUploadNow(){intnetworkLevelNetworkManager.getNetworkLevel();switch(networkLevel){caseNET_LEVEL_GOOD:// 网络好都可以传returntrue;caseNET_LEVEL_NORMAL:// 一般只传中优先级及以上returncurrentTask.getPriority()PRIORITY_MEDIUM;caseNET_LEVEL_WEAK:// 弱网只传高优先级returncurrentTask.getPriority()PRIORITY_HIGH;caseNET_LEVEL_BAD:caseNET_LEVEL_DISCONNECTED:// 很差不传returnfalse;default:returnfalse;}}四、失败重试与流量控制失败重试策略上传失败了怎么办跟之前的重连一样指数退避publicclassUploadRetryPolicy{privatestaticfinallongBASE_DELAY10*1000;// 10秒起步privatestaticfinallongMAX_DELAY10*60*1000;// 最多10分钟privatestaticfinalintMAX_RETRY10;// 最多重试10次privateintretryCount0;publiclonggetNextDelay(){if(retryCountMAX_RETRY){return-1;// 超过最大次数放弃}longdelay(long)(BASE_DELAY*Math.pow(2,retryCount));delayMath.min(delay,MAX_DELAY);retryCount;// 加随机抖动return(long)(delay*(0.8Math.random()*0.4));}publicvoidreset(){retryCount0;}}上传限速上传不能占满带宽不然影响MQTT等核心业务/** * 限速输出流控制上传速度 */publicclassRateLimitOutputStreamextendsOutputStream{privatelongbytesPerSecond;// 每秒最多传多少字节privatelongstartTime;privatelongbytesWritten;Overridepublicvoidwrite(byte[]b,intoff,intlen)throwsIOException{// 计算当前速度超了就sleep一下longelapsedSystem.currentTimeMillis()-startTime;if(elapsed0){longcurrentSpeedbytesWritten*1000/elapsed;if(currentSpeedbytesPerSecond){longsleepTime(bytesWritten*1000/bytesPerSecond)-elapsed;if(sleepTime0){try{Thread.sleep(sleepTime);}catch(InterruptedExceptione){}}}}out.write(b,off,len);bytesWrittenlen;}}限速策略正常业务期间限制在 200KB/s 以内不抢业务带宽闲时凌晨可以放开到 512KB/s 甚至更高弱网下进一步降低或者暂停上传五、闲时上传与流量管控闲时上传策略普通视频不着急留到流量便宜的时候传publicclassIdleTimeUploadScheduler{// 闲时段凌晨2点到6点privatestaticfinalintIDLE_START_HOUR2;privatestaticfinalintIDLE_END_HOUR6;/** * 判断当前是不是闲时 */publicbooleanisIdleTime(){inthourCalendar.getInstance().get(Calendar.HOUR_OF_DAY);returnhourIDLE_START_HOURhourIDLE_END_HOUR;}/** * 调度上传任务 */publicvoidscheduleUpload(UploadTasktask){if(task.getPriority()PRIORITY_HIGH){// 高优先级立刻传uploadQueue.addTask(task);}else{// 低优先级等闲时再传pendingIdleTasks.add(task);}}}月流量配额控制每个月流量有限超了就不传非必要的publicbooleanhasEnoughTrafficForUpload(longfileSize){longmonthUsedTrafficMonitor.getMonthTraffic();longmonthLimitConfigManager.getMonthTrafficLimit();longremainingmonthLimit-monthUsed;// 剩余流量超过文件大小的2倍才传留余量returnremainingfileSize*2;}六、云端溯源绑定视频传上去了得能跟订单、事件对应起来。上传时带上业务信息publicclassVideoUploadInfo{privateStringfileId;// 文件唯一IDprivateStringdeviceNo;// 设备编号privateStringeventType;// 事件类型open/abnormal/shakeprivateStringrelatedOrderId;// 关联订单号privatelongstartTime;// 录像开始时间privateintduration;// 时长privateStringfileMd5;// 文件MD5privatelongfileSize;// 文件大小}云端存储结构视频存储/ ├── 设备编号/ │ ├── 20260708/ │ │ ├── 订单号1.mp4 │ │ ├── 异常事件_时间戳.mp4 │ │ └── ... │ └── 20260709/ │ └── ...按设备日期分目录文件名带订单号或事件信息后台管理系统可以直接按订单号查视频。回调通知传完了通知后台后台把视频和订单绑定privatevoidonUploadSuccess(UploadTasktask){// 更新数据库状态VideoRecordDb.getInstance().updateUploadStatus(task.getFileId(),UPLOAD_STATUS_SUCCESS,System.currentTimeMillis());// 通知后台JSONObjectpayloadnewJSONObject();payload.put(file_id,task.getFileId());payload.put(video_url,task.getRemoteUrl());payload.put(related_order_id,task.getRelatedOrderId());payload.put(event_type,task.getEventType());MqttManager.publish(device/DeviceInfo.getDeviceNo()/video/uploaded,payload.toString(),1);}七、上传状态持久化APP重启了上传进度不能丢。上传任务持久化把待上传的任务存数据库CREATETABLEupload_task(idINTEGERPRIMARYKEYAUTOINCREMENT,file_idVARCHAR(64)UNIQUE,-- 文件IDfile_pathVARCHAR(256),-- 本地路径priorityINTEGER,-- 优先级event_typeVARCHAR(32),-- 事件类型related_idVARCHAR(64),-- 关联IDstatusINTEGERDEFAULT0,-- 0待上传 1上传中 2成功 3失败uploaded_chunksTEXT,-- 已上传分片列表JSONretry_countINTEGERDEFAULT0,-- 重试次数create_timeBIGINT,update_timeBIGINT);启动恢复APP启动时从数据库恢复未完成的任务publicvoidrestorePendingTasks(){ListUploadTaskpendingUploadTaskDb.getInstance().queryPendingTasks();for(UploadTasktask:pending){if(task.getStatus()UPLOAD_STATUS_UPLOADING){// 上次传了一半重置为待上传从断点继续task.setStatus(UPLOAD_STATUS_PENDING);}uploadQueue.addTask(task);}}八、踩坑经验坑1分片传完了但合并失败现象所有分片都显示传成功了但合并后的文件损坏原因某一片传的时候数据错了MD5没校验好解决每片传完服务端都校验MD5不对就让重传合并后再校验整个文件的MD5坑2上传过程中网络切换现象传一半WiFi切4GIP变了连接断了解决分片上传天然支持断点续传断了重新查进度接着传就行坑3视频还在录制就开始传现象文件还在写读出来的数据不完整解决录制完成、文件关闭后再加入上传队列上传前检查文件是否完整坑4大量视频同时上传把带宽占满现象批量恢复上传的时候几十个视频一起传MQTT都连不上了解决单队列串行上传同时只传一个加上传限速弱网下只传高优先级九、总结弱网视频上传的核心分片断点排队限速。分片上传512KB一片失败只重传一片省流量断点续传传了多少都记录重启断网都不怕优先级队列异常、投诉优先普通视频闲时再传上传限速不能抢业务带宽保证开门等核心功能优先流量控制月配额、闲时策略控制成本溯源绑定视频和订单、事件关联方便后台查询这套方案下来4G弱网环境下视频上传也能稳稳的不用再担心传一半断了白忙活。我是黒漂技术佬专注无人零售物联网实战。五大模块的12篇细分文章到这里就全部结束了下一篇是整个系列的收官总结把全链路的核心要点和踩坑经验串起来。有视频上传的坑欢迎私信交流大家一起避坑~

相关新闻

基于MATLAB图像处理的乳腺癌辅助检测系统设计与实现

基于MATLAB图像处理的乳腺癌辅助检测系统设计与实现

摘要:设计并实现一个基于数字图像处理技术的乳腺癌辅助检测系统,为临床诊断提供计算机辅助分析工具。项目概览项目简介本研究采用MATLAB平台,开发了一套完整的乳腺X光图像分析系统。系统采用中值滤波进行图像降噪,利用Otsu算法实现…

2026/7/12 0:35:21 阅读更多 →
Gradle 依赖冲突3种解决方案实测:强制指定 vs 排除 vs 停用自动解析

Gradle 依赖冲突3种解决方案实测:强制指定 vs 排除 vs 停用自动解析

Gradle依赖冲突实战指南:三大核心策略深度解析与性能对比1. 依赖冲突的本质与危害当我们在Gradle构建的Java项目中引入多个库时,经常会遇到一个令人头疼的问题:不同库对同一个子依赖声明了不同的版本要求。这种版本不一致会导致构建系统无法自…

2026/7/12 0:33:20 阅读更多 →
[Android] 聊天记录微备份 -聊天记录备份+本地数据备份

[Android] 聊天记录微备份 -聊天记录备份+本地数据备份

[Android] 聊天记录微备份 -聊天记录备份本地数据备份 链接:https://pan.xunlei.com/s/VOxF25z0t6y5zfDvHU8mWAI6A1?pwdd7ns# 安全备份覆盖多平台聊天文件备份,同时支持多类型数据备份,采用安全技术保障数据留存。图片备份针对平台文件提…

2026/7/12 0:31:20 阅读更多 →

最新新闻

Solarflare X2522/X3522 低延迟网卡实战:Onload/TCPDirect/ef_vi 三模式 32字节消息延迟对比

Solarflare X2522/X3522 低延迟网卡实战:Onload/TCPDirect/ef_vi 三模式 32字节消息延迟对比

Solarflare X2522/X3522 低延迟网卡三模式深度评测:从内核旁路到32字节消息实战在金融科技领域,每微秒的延迟优化都可能意味着数百万美元的盈亏差异。Solarflare X2522/X3522系列网卡凭借其创新的内核旁路技术,已成为高频交易系统中不可或缺的…

2026/7/12 1:35:52 阅读更多 →
Axure 10 中继器实战:3步构建可增删改查的动态数据表格

Axure 10 中继器实战:3步构建可增删改查的动态数据表格

Axure 10 中继器实战:3步构建可增删改查的动态数据表格在原型设计领域,中继器(Repeater)堪称Axure的"瑞士军刀"。这个看似简单的组件,却能模拟数据库的核心操作,让静态原型瞬间拥有动态交互的生命…

2026/7/12 1:35:52 阅读更多 →
一篇论文 AI 率高的段落集中在哪?怎么精准降下来

一篇论文 AI 率高的段落集中在哪?怎么精准降下来

一篇论文 AI 率高的段落集中在哪?怎么精准降下来 你现在大概是拿着一份检测报告,看着那个飘红的 AI 率发愁:明明整篇里很多字是自己写的,怎么就被判成一大半是 AI 生成的?更让你烦的是,你不知道到底改哪。…

2026/7/12 1:25:42 阅读更多 →
JAVA+Agentday11

JAVA+Agentday11

今日学习:IO流(人话->输入输出,Input->输入,Output->输出)IO流->字节流字符流序列化流打印流字节流(万能流)->一个字节或者字节数组的输入输出,构造->FileOutputStream(File file)//////////////FileOutputStream(String path) 方法->write特点;没有创…

2026/7/12 1:25:42 阅读更多 →
EM3080-W与TM4C129EKCPDT在工业条码识别中的硬件与固件设计

EM3080-W与TM4C129EKCPDT在工业条码识别中的硬件与固件设计

1. EM3080-W与TM4C129EKCPDT的硬件架构解析在工业级条码识别系统中,EM3080-W解码芯片与TM4C129EKCPDT微控制器的组合堪称黄金搭档。EM3080-W采用双核DSP架构,主核负责图像采集与预处理,工作频率高达120MHz,能够实时处理1280800分辨…

2026/7/12 1:25:42 阅读更多 →
C++工厂模式6种高级变形用法:从模板工厂到依赖注入容器实战解析

C++工厂模式6种高级变形用法:从模板工厂到依赖注入容器实战解析

1. 项目概述:工厂模式在大型C项目中的真实价值 如果你写过几年C,尤其是在参与过几十万行、上百个模块的大型项目之后,回头再看设计模式,感受会完全不同。教科书里那些“创建型”、“结构型”的标签,在真实项目里会演变…

2026/7/12 1:23:41 阅读更多 →

日新闻

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨 在一个阳光明媚的上午,互联网大厂的面试官坐在桌前,准备迎接他的面试候选人——燕双非,一个以搞笑和幽默著称的程序员。第一轮提问 面试官:燕双非,作…

2026/7/12 0:03:13 阅读更多 →
车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估

车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估

车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估在智能驾驶和车联网技术快速发展的今天,车载以太网作为新一代车载网络的核心传输技术,其物理层性能直接决定了数据传输的可靠性和稳定性。1000BASE-T1作为当前主流的…

2026/7/12 0:03:14 阅读更多 →
VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战,5步完成Keil工程转换

VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战,5步完成Keil工程转换

VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战指南嵌入式开发领域正经历一场工具链的静默革命。当传统Keil用户首次打开VSCode的扩展市场搜索EIDE时,往往会惊讶于这个看似简单的插件竟能重构十余年的开发习惯。本文将揭示如何用五个精准步骤&#xff0…

2026/7/12 0:03:14 阅读更多 →

周新闻

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨 在一个阳光明媚的上午,互联网大厂的面试官坐在桌前,准备迎接他的面试候选人——燕双非,一个以搞笑和幽默著称的程序员。第一轮提问 面试官:燕双非,作…

2026/7/12 0:03:13 阅读更多 →
车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估

车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估

车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估在智能驾驶和车联网技术快速发展的今天,车载以太网作为新一代车载网络的核心传输技术,其物理层性能直接决定了数据传输的可靠性和稳定性。1000BASE-T1作为当前主流的…

2026/7/12 0:03:14 阅读更多 →
VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战,5步完成Keil工程转换

VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战,5步完成Keil工程转换

VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战指南嵌入式开发领域正经历一场工具链的静默革命。当传统Keil用户首次打开VSCode的扩展市场搜索EIDE时,往往会惊讶于这个看似简单的插件竟能重构十余年的开发习惯。本文将揭示如何用五个精准步骤&#xff0…

2026/7/12 0:03:14 阅读更多 →

月新闻