SuperAPI AI 工具集

parallel-chunked-download

编程与开发

查看项目

更新于 10-17 16:16

技能介绍

用途

该技能面向用 Kotlin 协程实现“单文件并行分块下载”的场景:把一次下载拆成 N 个字节区间请求并发执行,每个分片写入自己的临时文件,最后按索引顺序拼接为目标文件,并以 Flow 形式对外报告进度。

主要能力与流程

  1. 能力探测:先用 head(url) { header("Range", "bytes=0-0") } 探测服务器是否支持 Range,并读取 Content-Length 获取文件大小;探测异常时降级。
  2. 分支决策:仅当支持 Range 且 fileSize > CHUNK_SIZE * 2 时走并行分块下载,否则走单线程下载。
  3. 并行分片:在 coroutineScope 内为每个分片启动独立任务,计算 startByte/endByte(末片结束于 fileSize - 1),请求带 Range: bytes=start-end,要求响应为 206,并把响应体流式写入各自的临时文件。
  4. 分片级重试:每个分片维护独立尝试计数,失败后按指数退避重试(上限 10 秒),超过 maxRetries 抛出异常。
  5. 合并与清理:所有分片完成后按索引顺序拼接临时文件为目标文件,再删除临时目录。
  6. 进度上报:通过 channelFlow 与 trySend(Progress.running(p))、trySend(Progress.done(ok)) 推送进度与完成信号。

主要陷阱(技能重点内容)

  • 探测误判:支持 Range 的服务器对 bytes=0-0 返回 206 且带 Content-Range;不支持的返回 200 全文。接受任意 2xx 会把“忽略 Range”当作“支持 Range”。应以 206 或 Content-Range 为准。
  • 分片仍需校验 206:探测与真实请求可能因缓存或重定向不一致;若服务器对分片请求返回 200 全文,N 个任务各写全文,合并后文件长度变为 N 倍且无任何报错。
  • 重试粒度:单个分片失败只重试该分片,而不是整个文件——这正是每片独立临时文件的意义。
  • 重试必须截断重写:重开临时文件要 truncate,不能 append;否则该分片变成 1.5 倍长度,合并结果错位损坏。
  • 末片边界:fileSize / parallel 整数除法丢余数,末片必须结束于 fileSize - 1,否则尾部字节丢失。
  • 进度发送结果:trySend 返回是否被接受;进度 tick 丢弃无害,但终止信号不能被丢弃。完成状态应通过 Flow 自身完成来表达,channel 只用于进度。
  • 进度粒度:按分片计数会以 N 步跳变,且因 chunkSize 截断停在略低于 1.0(“卡在 99%”)。若进度条重要,应按字节计数,并用原子计数避免并发重复计数。
  • 并行度上下限:低于阈值时两次探测往返的开销大于并发收益(阈值是固定下限常量的两倍);过高则在链路饱和、移动网络或按连接限速的主机上反而变差。
  • 失败路径:catch 与合并后都要清理临时目录;不要把异常存入变量后发出“已完成、进度 0”却不再读取,否则收集方无法区分失败与零字节成功。该设计不跨进程重启恢复,续传属于另一项功能。

验证方式

技能给出四条 grep 检查:查找 header("Range"、查找 206 比较、列出所有 trySend(、统计其中检查了结果(isSuccess|isFailure|getOrNull)的数量,用于发现探测无 206 校验、进度发送无人检查等问题。分片边界可用算术测试:选取不能被分片数整除的文件大小,计算每对 start/end,断言无缝无重叠地覆盖 0..fileSize-1。

适用场景与限制

适用于需要在 Kotlin 项目中实现可观测进度的多分片并发下载,且目标服务器支持 Range 的场景。技能本身是经验与陷阱说明,未提供可直接安装的软件包、收费信息或完整可运行项目;提到的 bare HTTP 客户端细节在另一技能 ktor-kmp-client-architecture 中描述。