HarmonyOS 文件传输实战:上传下载队列、断点续传与失败恢复怎么设计
2026/7/20 11:40:15 网站建设 项目流程

HarmonyOS 文件传输实战:上传下载队列、断点续传与失败恢复怎么设计

文件传输最怕“接口能跑,体验不稳”。真实项目里,用户下载离线地图、上传日志包、同步大文件时,网络可能断开,应用可能切后台,用户可能暂停或取消,服务端也可能返回分片过期。如果只写一个download(url)upload(file),失败后很难恢复,也说不清楚当前文件到底传到哪里。

这篇文章只解决一个工程问题:HarmonyOS 应用里如何把上传、下载、队列、断点续传、失败恢复和日志验收设计成一条可维护链路。

本文会落到四个结果:

  1. 每个传输任务都有唯一 id、状态、进度和错误原因。
  2. 上传下载都进入队列,不让多个大任务把网络和内存打爆。
  3. 断点续传记录分片进度,失败后能从最近可用位置恢复。
  4. 用户取消、网络失败、服务端过期都有明确回退策略。

一、先区分三种失败:网络失败、业务失败、用户取消

文件传输失败不能只显示“失败”。三种失败的处理完全不同。

类型例子处理方式
网络失败弱网、断网、超时可重试,保留进度
业务失败文件不存在、权限不足、分片过期停止任务,提示原因
用户取消用户手动暂停或取消按用户意图保存或清理

如果这三类都混成一个error,后面就会出现两个问题:用户不知道能不能重试,开发也不知道该从哪里恢复。

二、资料与版本边界:本文写应用层传输管理

本文示例面向 HarmonyOS NEXT / ArkTS 工程,应用层重点放在任务队列、分片记录、网络恢复、进度持久化和 UI 状态管理。具体网络请求可结合@ohos.net.http、上传下载能力或团队已有网络库落地。

层级本文关注不展开
任务层上传、下载、暂停、取消、恢复底层 TCP 实现
队列层并发数、等待、重试复杂调度算法
进度层分片、已完成字节、校验服务端存储实现
验证层弱网、断点、后台、取消压测平台搭建

三、任务模型:别只保存 URL

先定义统一任务模型。上传和下载都可以复用同一套状态。

exporttypeTransferType='upload'|'download';exporttypeTransferStatus='waiting'|'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;}

这个模型解决四个问题:

  1. taskId用于日志、UI 和恢复。
  2. finishedBytes让断点续传有依据。
  3. retryCount避免无限重试。
  4. status让页面不用猜任务阶段。

四、队列控制:大文件传输不能全部并发

大文件上传下载要控制并发,尤其是移动网络和后台场景。

exportclassTransferQueue{privatewaiting:TransferTask[]=[];privaterunning=newMap<string,TransferTask>();constructor(privatereadonlymaxRunning:number){}enqueue(task:TransferTask):void{this.waiting.push(task);}next():TransferTask[]{constpicked:TransferTask[]=[];while(this.running.size<this.maxRunning&&this.waiting.length>0){consttask=this.waiting.shift();if(task===undefined){break;}task.status='running';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{privatedata=newMap<string,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{constcheckpoint=store.get(task.taskId);conststartOffset=checkpoint!==undefined?checkpoint.offset:task.finishedBytes;return{taskId:task.taskId,url:task.sourceUri,savePath:task.targetUri,startOffset};}

注意两点:

  1. 服务端必须支持范围请求或业务层分片下载,否则客户端无法真正断点。
  2. 恢复前要校验本地临时文件是否存在,不能只相信进度数字。

七、上传分片:每片成功后再保存进度

上传大文件更适合分片。每片成功后保存 checkpoint。

exportinterfaceUploadChunk{taskId:string;chunkIndex:number;start:number;end:number;checksum:string;}exportfunctioncreateUploadChunks(task:TransferTask,chunkSize:number):UploadChunk[]{constchunks:UploadChunk[]=[];letstart=0;letindex=0;while(start<task.totalBytes){constend=Math.min(start+chunkSize,task.totalBytes);chunks.push({taskId:task.taskId,chunkIndex:index,start,end,checksum:`${task.taskId}_${index}_${end-start}`});start=end;index+=1;}returnchunks;}

这段代码里的checksum只是示例占位。真实项目应使用可靠摘要算法,并和服务端约定校验规则。上传分片如果没有校验,弱网重试时很容易产生重复片或坏片。

八、重试策略:失败不是无限重试

重试要有边界,也要区分失败类型。

exporttypeTransferFailType='network'|'server'|'permission'|'expired'|'unknown';exportinterfaceRetryDecision{shouldRetry:boolean;delayMs:number;message:string;}exportfunctionresolveRetryDecision(type:TransferFailType,retryCount:number):RetryDecision{if(type==='permission'||type==='expired'){return{shouldRetry:false,delayMs:0,message:'当前任务无法恢复,需要重新发起'};}if(retryCount>=3){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{constpercent=task.totalBytes===0?0:Math.floor((task.finishedBytes/task.totalBytes)*100);if(task.status==='running'){return{title:'传输中',progressText:`${percent}%`,primaryAction:'pause',secondaryAction:'cancel'};}if(task.status==='paused'){return{title:'已暂停',progressText:`${percent}%`,primaryAction:'resume',secondaryAction:'cancel'};}if(task.status==='failed'){constmessage=task.errorMessage!==undefined?task.errorMessage:'请稍后重试';return{title:'传输失败',progressText:message,primaryAction:'retry',secondaryAction:'delete'};}if(task.status==='success'){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 能追踪全链路
弱网已验证模拟断网、慢网、切后台都通过

验收一定要覆盖异常路径。文件传输不是“下载成功一次”就算完成。

十三、文件传输相关官方资料

  1. 华为开发者文档:Network Kit / 网络请求
    https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/network-kit-overview
  2. 华为开发者文档:http 请求能力
    https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-http
  3. 华为开发者文档:应用文件访问与管理
    https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/app-file-access
  4. 华为开发者文档:后台任务
    https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/background-task-overview

十四、把传输做成可恢复任务

上传下载的核心不是“发起请求”,而是“任务可恢复”。只要任务模型、队列、checkpoint、重试、UI 和日志都完整,文件传输就能从一次脆弱请求变成可维护能力。

最后用这张表做复盘:

问题稳定答案
任务是谁taskId唯一标识
传到哪里finishedBytes和 checkpoint 记录
失败怎么办按失败类型决定重试或重建
用户取消怎么办停止请求并清理临时文件
怎么证明稳定弱网、断网、重启、切后台全走一遍

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询