HarmonyOS 鸿蒙Next中API18使用request.agent实现的断点下载不能在原文件上续写
HarmonyOS 鸿蒙Next中API18使用request.agent实现的断点下载不能在原文件上续写 当我下载部分文件后,使用task.stop暂停下载后,退出杀死应用后台。再次进去应用。想要继续下载文件。我记录了暂停时的文件下载进度。然后从这个进度开始断点下载。发现无法在原文件上进行续写。这是为什么。有没有解决办法?
【解决方案】
1、断点续传:在 create 任务时记录Task任务 id。后续重启应用后,通过 request.agent.getTask 获取任务,然后使用request.agent.show查询任务状态,如果任务正在进行,重新挂一遍监听回调就行。
2、使用request.agent.create创建下载任务,也可用pause、resume接口来实现下载任务的暂停与恢复,代码参考:
import { BusinessError, request } from '@kit.BasicServicesKit';
let config: request.agent.Config = {
action: request.agent.Action.DOWNLOAD,
url: 'https://127.0.0.1', // 需要手动将url替换为真实服务器的HTTP协议地址
mode: request.agent.Mode.BACKGROUND,
overwrite: false,
method: "GET"
};
// 请在组件内获取context,确保this.getUIContext().getHostContext()返回结果为UIAbilityContext
let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
request.agent.create(context, config).then((task: request.agent.Task) => {
// 暂停下载
task.pause((err: BusinessError) => {
if (err) {
console.error(`Failed to pause the download task, Code: ${err.code}, message: ${err.message}`);
return;
}
console.info(`Succeeded in pausing a download task. `);
});
// 恢复下载
task.resume((err: BusinessError) => {
if (err) {
console.error(`Failed to resume the download task, Code: ${err.code}, message: ${err.message}`);
return;
}
console.info(`Succeeded in resuming a download task. `);
});
}).catch((err: BusinessError) => {
console.error(`Failed to create a download task, Code: ${err.code}, message: ${err.message}`);
});
更多关于HarmonyOS 鸿蒙Next中API18使用request.agent实现的断点下载不能在原文件上续写的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
问题原因分析
- 任务模式问题:如果下载任务创建为前端任务(
mode: request.agent.Mode.FOREGROUND),任务生命周期与应用绑定,应用被杀死后任务会自动移除,导致无法恢复:
前端任务是立即的、模态界面的、同步的,跟随应用的生命周期。 后台任务为可等待的、任意界面的、异步的,通常数据量较大、耗时长,优先级相较于前端任务低且与应用生命周期无关。
-
文件覆盖设置:如果配置中
overwrite: true,恢复下载时会重新创建文件(覆盖已下载部分),而不是续写。DownloadConfig的overwrite参数控制是否覆盖现有文件。 -
任务状态持久化不足:应用被杀死后,若未主动保存任务ID(
tid)或进度信息,系统可能无法自动关联到原有任务:
后台任务支持断点续传,但需通过任务查询接口恢复任务状态。
- 服务器支持:断点续传需要服务器支持
Range请求(HTTP状态码206),如果服务器不支持,续传会失败。
解决方案
步骤1:创建后台任务并正确配置
- 使用
@ohos.request模块创建后台任务(mode: request.agent.Mode.BACKGROUND),确保任务与应用生命周期无关。 - 设置
overwrite: false,避免覆盖已下载文件。 - 显式指定断点参数(
begins和ends)以支持续传(可选,模块通常自动处理)。
示例代码:
let config: request.agent.Config = {
action: request.agent.Action.DOWNLOAD,
url: ' https://example.com/file.zip',
mode: request.agent.Mode.BACKGROUND, // 必须为后台任务
overwrite: false, // 禁止覆盖,允许续写
saveas: './download/file.zip',
network: request.agent.Network.ANY
};
let task = await request.agent.create(context, config);
task.start();
步骤2:暂停时保存任务状态
- 使用
task.pause()而非task.stop(),因为pause()更适用于暂停恢复(stop()需用start()恢复,但任务可能被系统清理)。 - 持久化保存任务ID(
tid)和已下载进度(receivedSize),例如使用本地存储或数据库。
示例代码:
// 暂停任务
task.pause();
// 保存任务ID和进度
let taskId = task.tid;
let progress = await getDownloadProgress(task); // 自定义获取进度的方法
saveToStorage(taskId, progress); // 持久化保存
步骤3:应用重启后恢复任务
- 通过
request.agent.getTasks()查询所有任务,或使用保存的tid通过request.agent.getTaskInfo(tid)获取任务状态。 - 如果任务存在且状态为暂停(
status: PAUSED),直接调用task.resume()恢复。 - 如果任务不存在(如被系统清理),重新创建任务并设置
begins参数为已下载大小。
示例代码:
// 查询现有任务
let tasks = await request.agent.getTasks({ action: request.agent.Action.DOWNLOAD });
let existingTask = tasks.find(t => t.tid === savedTaskId);
if (existingTask) {
// 任务存在,直接恢复
existingTask.resume();
} else {
// 重新创建任务,设置断点
let newConfig: request.agent.Config = {
...config,
begins: savedProgress.receivedSize // 从保存的进度开始
};
let newTask = await request.agent.create(context, newConfig);
newTask.start();
}
步骤4:验证服务器支持
- 确保服务器支持
Range请求(检查HTTP响应头是否包含Accept-Ranges: bytes)。 - 测试时可通过抓包工具确认请求是否包含
Range: bytes=<start>-字段。
如果问题仍存在,请检查日志中是否有错误码(如13400001文件IO错误),并参考文档中的错误码表进行排查。
在HarmonyOS Next API 18中,使用request.agent实现的断点下载功能无法直接覆盖原文件进行续写。这是由于系统文件安全机制限制了直接修改已存在的下载文件。断点下载时需创建新的临时文件存储增量数据,下载完成后再通过文件操作替换原文件。可通过File API检查文件状态,使用分段写入方式处理下载数据,确保数据完整性。
在HarmonyOS Next API 18中,使用request.agent进行断点下载时无法在原文件上续写,主要是因为文件系统在应用被杀死后可能无法保持文件句柄的连续性。当应用重新启动时,之前打开的文件可能已被系统清理或处于不可写状态。
建议采用以下方案解决:
- 使用
fs.open以读写模式(如rw+)重新打开文件,并通过fs.seek定位到已下载的字节位置。 - 在请求头中设置
Range字段,例如Range: bytes=已下载大小-,确保服务器支持断点续传。 - 将新数据追加到文件指定位置,而非覆盖原文件。
示例代码片段:
// 重新打开文件并定位
let file = await fs.open('path/to/file', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
await fs.seek(file.fd, 已下载大小, 0);
// 设置Range头
let headers = { 'Range': `bytes=${已下载大小}-` };
// 发起请求并写入数据流
注意检查服务器是否返回206 Partial Content状态码,同时确保应用有足够的文件读写权限。

