HarmonyOS 文件传输实战:上传下载队列、断点续传与失败恢复怎么设计 HarmonyOS 文件传输实战上传下载队列、断点续传与失败恢复怎么设计文件传输最怕“接口能跑体验不稳”。真实项目里用户下载离线地图、上传日志包、同步大文件时网络可能断开应用可能切后台用户可能暂停或取消服务端也可能返回分片过期。如果只写一个download(url)或upload(file)失败后很难恢复也说不清楚当前文件到底传到哪里。这篇文章只解决一个工程问题HarmonyOS 应用里如何把上传、下载、队列、断点续传、失败恢复和日志验收设计成一条可维护链路。本文会落到四个结果每个传输任务都有唯一 id、状态、进度和错误原因。上传下载都进入队列不让多个大任务把网络和内存打爆。断点续传记录分片进度失败后能从最近可用位置恢复。用户取消、网络失败、服务端过期都有明确回退策略。一、先区分三种失败网络失败、业务失败、用户取消文件传输失败不能只显示“失败”。三种失败的处理完全不同。类型例子处理方式网络失败弱网、断网、超时可重试保留进度业务失败文件不存在、权限不足、分片过期停止任务提示原因用户取消用户手动暂停或取消按用户意图保存或清理如果这三类都混成一个error后面就会出现两个问题用户不知道能不能重试开发也不知道该从哪里恢复。二、资料与版本边界本文写应用层传输管理本文示例面向 HarmonyOS NEXT / ArkTS 工程应用层重点放在任务队列、分片记录、网络恢复、进度持久化和 UI 状态管理。具体网络请求可结合ohos.net.http、上传下载能力或团队已有网络库落地。层级本文关注不展开任务层上传、下载、暂停、取消、恢复底层 TCP 实现队列层并发数、等待、重试复杂调度算法进度层分片、已完成字节、校验服务端存储实现验证层弱网、断点、后台、取消压测平台搭建三、任务模型别只保存 URL先定义统一任务模型。上传和下载都可以复用同一套状态。exporttypeTransferTypeupload|download;exporttypeTransferStatuswaiting|running|paused|success|failed|cancelled;exportinterfaceTransferTask{taskId:string;type:TransferType;sourceUri:string;targetUri:string;totalBytes:number;finishedBytes:number;status:TransferStatus;retryCount:number;errorMessage?:string;updatedAt:number;}这个模型解决四个问题taskId用于日志、UI 和恢复。finishedBytes让断点续传有依据。retryCount避免无限重试。status让页面不用猜任务阶段。四、队列控制大文件传输不能全部并发大文件上传下载要控制并发尤其是移动网络和后台场景。exportclassTransferQueue{privatewaiting:TransferTask[][];privaterunningnewMapstring,TransferTask();constructor(privatereadonlymaxRunning:number){}enqueue(task:TransferTask):void{this.waiting.push(task);}next():TransferTask[]{constpicked:TransferTask[][];while(this.running.sizethis.maxRunningthis.waiting.length0){consttaskthis.waiting.shift();if(taskundefined){break;}task.statusrunning;this.running.set(task.taskId,task);picked.push(task);}returnpicked;}finish(taskId:string):void{this.running.delete(taskId);}}这段队列不是为了炫技而是保护体验。一次跑太多下载进度可能都很慢失败率更高内存也更容易上涨。五、断点记录恢复靠数据不靠记忆断点续传要记录每个任务完成到哪里。下载通常记录已完成字节上传通常记录已上传分片。exportinterfaceTransferCheckpoint{taskId:string;offset:number;chunkSize:number;chunkIndex:number;checksum?:string;savedAt:number;}exportclassCheckpointStore{privatedatanewMapstring,TransferCheckpoint();save(checkpoint:TransferCheckpoint):void{this.data.set(checkpoint.taskId,checkpoint);}get(taskId:string):TransferCheckpoint|undefined{returnthis.data.get(taskId);}remove(taskId:string):void{this.data.delete(taskId);}}真实项目里可以把CheckpointStore换成 Preferences、关系型数据库或文件。关键是不能只存在内存里否则应用重启后就无法恢复。六、下载恢复从 offset 开始而不是重新下载下载恢复时要读取 checkpoint再从对应位置请求。exportinterfaceDownloadRequest{taskId:string;url:string;savePath:string;startOffset:number;}exportfunctionbuildDownloadRequest(task:TransferTask,store:CheckpointStore):DownloadRequest{constcheckpointstore.get(task.taskId);conststartOffsetcheckpoint!undefined?checkpoint.offset:task.finishedBytes;return{taskId:task.taskId,url:task.sourceUri,savePath:task.targetUri,startOffset};}注意两点服务端必须支持范围请求或业务层分片下载否则客户端无法真正断点。恢复前要校验本地临时文件是否存在不能只相信进度数字。七、上传分片每片成功后再保存进度上传大文件更适合分片。每片成功后保存 checkpoint。exportinterfaceUploadChunk{taskId:string;chunkIndex:number;start:number;end:number;checksum:string;}exportfunctioncreateUploadChunks(task:TransferTask,chunkSize:number):UploadChunk[]{constchunks:UploadChunk[][];letstart0;letindex0;while(starttask.totalBytes){constendMath.min(startchunkSize,task.totalBytes);chunks.push({taskId:task.taskId,chunkIndex:index,start,end,checksum:${task.taskId}_${index}_${end-start}});startend;index1;}returnchunks;}这段代码里的checksum只是示例占位。真实项目应使用可靠摘要算法并和服务端约定校验规则。上传分片如果没有校验弱网重试时很容易产生重复片或坏片。八、重试策略失败不是无限重试重试要有边界也要区分失败类型。exporttypeTransferFailTypenetwork|server|permission|expired|unknown;exportinterfaceRetryDecision{shouldRetry:boolean;delayMs:number;message:string;}exportfunctionresolveRetryDecision(type:TransferFailType,retryCount:number):RetryDecision{if(typepermission||typeexpired){return{shouldRetry:false,delayMs:0,message:当前任务无法恢复需要重新发起};}if(retryCount3){return{shouldRetry:false,delayMs:0,message:重试次数已达上限请稍后手动重试};}return{shouldRetry:true,delayMs:1000*Math.pow(2,retryCount),message:网络异常稍后自动重试};}重试次数、退避间隔要可配置。无限重试会增加耗电也会让用户误以为任务卡住。九、页面状态用户要能暂停、继续、取消文件传输页面至少要展示状态、进度和可执行动作。exportinterfaceTransferUiState{title:string;progressText:string;primaryAction:pause|resume|retry|open|none;secondaryAction:cancel|delete|none;}exportfunctionbuildTransferUiState(task:TransferTask):TransferUiState{constpercenttask.totalBytes0?0:Math.floor((task.finishedBytes/task.totalBytes)*100);if(task.statusrunning){return{title:传输中,progressText:${percent}%,primaryAction:pause,secondaryAction:cancel};}if(task.statuspaused){return{title:已暂停,progressText:${percent}%,primaryAction:resume,secondaryAction:cancel};}if(task.statusfailed){constmessagetask.errorMessage!undefined?task.errorMessage:请稍后重试;return{title:传输失败,progressText:message,primaryAction:retry,secondaryAction:delete};}if(task.statussuccess){return{title:传输完成,progressText:100%,primaryAction:open,secondaryAction:delete};}return{title:等待中,progressText:${percent}%,primaryAction:none,secondaryAction:cancel};}UI 状态不要散落在页面判断里。统一函数能保证上传和下载的操作含义一致。十、日志记录排查要能看到完整链路文件传输问题经常跨越网络、存储、后台和服务端必须有日志。exportinterfaceTransferLog{taskId:string;action:enqueue|start|progress|pause|resume|retry|success|fail|cancel;status:TransferStatus;finishedBytes:number;message:string;timestamp:number;}exportfunctioncreateTransferLog(task:TransferTask,action:TransferLog[action],message:string):TransferLog{return{taskId:task.taskId,action,status:task.status,finishedBytes:task.finishedBytes,message,timestamp:Date.now()};}排查时至少要能回答任务什么时候创建、什么时候开始、失败前完成了多少、是否保存 checkpoint、是否自动重试、用户是否取消。十一、文件传输问题排查表现象优先怀疑检查方式修复方向断网后从头下载checkpoint 没保存查CheckpointStore每次进度变化后持久化上传重复分片服务端幂等不足查 chunkIndex 和 checksum上传前查询已完成分片任务越跑越多队列无并发限制查 running 数量加maxRunning用户取消后还在传取消状态没传到底层查 cancel 日志取消后停止请求并清理临时文件失败原因不清楚error 太泛查失败类型区分 network/server/permission切后台后状态丢失进度只存在内存重启应用验证持久化任务和 checkpoint不要把所有问题都当成“网络差”。弱网只是触发器真正的问题通常是状态没有保存。十二、上线前传输验收表检查项通过标准队列并发受控同时运行任务数量不超过阈值断点可恢复断网、重启后能从 checkpoint 继续用户可暂停取消UI 操作和任务状态一致失败有分类网络、权限、过期、服务端错误可区分临时文件可清理取消和失败不会留下垃圾文件日志可串联一个 taskId 能追踪全链路弱网已验证模拟断网、慢网、切后台都通过验收一定要覆盖异常路径。文件传输不是“下载成功一次”就算完成。十三、文件传输相关官方资料华为开发者文档Network Kit / 网络请求https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/network-kit-overview华为开发者文档http 请求能力https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-http华为开发者文档应用文件访问与管理https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/app-file-access华为开发者文档后台任务https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/background-task-overview十四、把传输做成可恢复任务上传下载的核心不是“发起请求”而是“任务可恢复”。只要任务模型、队列、checkpoint、重试、UI 和日志都完整文件传输就能从一次脆弱请求变成可维护能力。最后用这张表做复盘问题稳定答案任务是谁taskId唯一标识传到哪里finishedBytes和 checkpoint 记录失败怎么办按失败类型决定重试或重建用户取消怎么办停止请求并清理临时文件怎么证明稳定弱网、断网、重启、切后台全走一遍