频道 TurboDL —— 给 JVM 的多线程下载引擎
版本v0.4.1.0 API1.0.0 测试199 通过

HTTP/RANGEKOTLIN JVM 17+无需界面

各下一段,合成一份。

Kotlin/JVM 下载引擎把一次 HTTP 传输拆给 16 个工作单元,从磁盘上真实落下的字节续传,并拒绝在 Range 上撒谎的服务端。

版本
v0.4.1.0
引擎
1 次请求 → 16 个分片
校验
199 项测试

一次请求,变成 16 个移动部件

进度条不是装饰。它的三段分别对应真实流程:探测、并行分片、校验。

01

1次探测

下载开始前先问一次:这个服务端认不认字节范围 Range?

02

16个分片

16 个分片同时进行;工作窃取会把剩余工作从慢连接手里拿走。

03

1次校验

先核对每一段实际返回的字节,再核对合并文件的预期总大小。

  1. 请求之前

    先确认服务端是否支持 Range。不支持就不假装支持,退回整条下载。

  2. 下载进行时

    块数约为连接数的四倍,慢连接不会独占最后剩下那块工作。

  3. 说完成之前

    实际返回的字节要和请求的 range 对上;合并后的文件还要和总大小对上。

字节层面的六个决定

机制实际结果
分片并发下载按 HTTP Range 并行下载分片,并复用连接(HTTP/2 多路复用 + keep-alive)。
动态分片 + 工作窃取细粒度预分片 + 工作窃取:一条慢连接拖不垮整个传输,也消除了「最后一个分片单线程收尾」的长尾。
克制的自适应只在遇到 429/503 或连续失败时才降低并发。普通抖动绝不改线程数 —— 不做 AIMD 来回震荡。
按真实进度续传分片状态落盘;暂停/继续按磁盘上真实写完的字节接着下,而不是按记忆中的数字。
拒绝 Range 篡改会核对服务端实际返回的字节是否就是请求的那段 Range —— 忽略 Range 直接回整文件的服务端会被识别出来,而不是把错位数据写进文件。
字节级完整性校验合并完成后校验总大小;少了或多了都判为失败。

每一条都能在仓库里核对。其中两条存在的原因,是「看起来显然」的做法被实测否掉了。

每个控件都对应公开字段

下面五个控件使用引擎默认值。开关可以在页面里直接拨动,数字结论来自实测。

dynamicSegmentation

把工作切得比连接数更细。

约四条连接对应四块工作,收尾阶段不会只剩一个工作单元。关闭后退回固定块大小 + 工作窃取。

默认开启

tailAssist

把尾巴交给空闲的工作单元。

只在收尾阶段生效,每个分片最多拆两次;低于 2 MiB 不拆,16 MiB 的块最多变成四段。

默认开启

overlapMerge

最后几个字节还在路上时先合并。

512 MB / 64 连接实测:11.08 s 降到 10.39 s,约 6%。与收尾托管同时开启时可能重复写,所以默认关闭。

默认关闭

bufferedSegmentWrite

分片写入走缓冲流。

16 MB / 128 连接 / 64 KB 分片:直写 2576 ms、缓冲 2584 ms,0.3% 落在噪声里;不加它,续传进度更可靠。

默认关闭

trustAllCerts

信任任何证书。

只给抓包调试用。它关掉证书校验,生产环境保持关闭。

默认关闭

能力配置项说明
全局限速globalSpeedLimitBytesPerSec0 = 不限速
线程数maxConnectionsPerTask1..256
并发任务数maxConcurrentTasks1..64
最大重试maxRetries0..50
动态分片dynamicSegmentationtrue / false
代理proxy直连 / 系统 / 手动(HTTP,SOCKS,鉴权) / PAC
DNSdns系统 / 静态 hosts / DoH(url)
忽略 SSLtrustAllCerts抓包调试用
主题—由上层应用负责;SDK 不涉及界面

SDK 有意不画界面:限速、队列与进度都是值,怎么呈现由上层应用决定。

一个仓库,七个刻意分开的构件

可以只取引擎,也可以装配完整协议栈。依赖方向始终不会反转。

  1. turbodl-core引擎 / 零插件依赖
  2. turbo-plugin-runtime生命周期 + 扩展点
  3. turbo-plugin-bootstrap一次调用完成装配
  4. turbo-plugin-hlsHLS 点播后端
  5. turbo-plugin-jsQuickJS 加载器
  6. turbo-plugin-parser分享链接解析
  7. turbodl-cli可脚本化命令行
构件作用必需?
turbodl-core下载引擎本体
分片并发下载、断点续传、重试、限速、DNS/代理。可单独使用,不依赖插件框架。
必需
turbo-plugin-runtime插件运行时内核
生命周期、清理(disposer)、事件总线、服务注册表、扩展点、版本握手、诊断。对任何具体协议一无所知。
可选
turbo-plugin-bootstrap基础装配
一次调用装配基础插件(Kotlin 加载器 + HTTP 后端),并桥接引擎的事件流。
可选
turbo-plugin-hlsHLS VOD 适配
解析 M3U8 清单、分片并发下载(分片级重试)、AES-128 解密、支持 EXT-X-BYTERANGE。直播/DRM/fMP4 清单会被明确拒绝,而不是产出一个损坏的文件。
可选
turbo-plugin-jsJavaScript 插件加载器
加载用 JavaScript 写的 TurboDL 插件。内嵌 QuickJS —— 每个脚本一个 runtime 与 context —— 并给脚本一套稳定的 `host`/`plugin` ABI。
可选
turbo-plugin-parser平台链接解析
把分享链接变成可下载请求:微博(MP4 + 图集)、小红书(无水印视频 + 原图)、Pixiv(每页原图)。不持有任何凭证 — 平台需要登录时返回 null,由宿主自己的会话处理接手。
可选
turbodl-cli命令行工具
基于 SDK 的完整命令行工具:多线程、断点续传、批量、给脚本与 Agent 用的 NDJSON 输出。以 fat JAR 形式提供。
可选

失败即拒绝明确不支持

它拒绝假装支持什么

不支持的流会响亮地失败。另一种做法 —— 写下一个看起来正常、其实不对的文件 —— 正是这一节要避免的失败方式。

插件明确拒绝为什么
HLS VOD Backendbackend.hls直播清单(没有 `EXT-X-ENDLIST`)DRM / `SAMPLE-AES`fMP4 `EXT-X-MAP`不连续标记明确拒绝:不支持的流直接失败,而不是产出一个看起来正常、其实不对的文件。
JavaScript Plugin Loaderloader.js注册 `DownloadBackend`(字节面留在 Kotlin 一侧)发布服务(服务是 Kotlin 对象图)`require` / npm / `fetch` —— 宿主 ABI 就是全部可用面`jar:` 与远程脚本源(目前只支持本地文件 / `inline:` / `data:`)脚本负责算 URL、请求头和签名,从不经手下载数据流。这条边界正是它不会变成带宽瓶颈的原因。
Platform Link Parserparser.platforms登录 — 需要会话的平台返回 null,由宿主自己的账号处理接手Pixiv 动图(ugoira):zip + 帧时长表需要图像处理Pixiv 小说需要签名的接口(抖音 / 快手 / B站):各自的签名算法仍留在宿主侧它的结果类型不含任何路径:`DownloadRequest` 需要 `destination: File`,而解析发生在宿主决定目录之前。临时目录占位只在边界处补上,宿主仍然走自己的路径策略。

引擎运行时会发出什么

命令行可以每个事件打印一行 JSON。下面的面板和终端使用引擎同一批字段名。

进度事件62%
statedownloading
downloaded212.4 MB
total341.8 MB
percent62
speed18.6 MB/s
connections16
eta_ms7 020

每个字段都来自引擎事件流;SDK 自己不画任何界面。

NDJSON / STDOUTturbodl --json · 每个事件一行
{"type":"start","url":"https://example.com/image.iso","output":"/tmp/image.iso"}
{"type":"timing","stage":"metadata","t_ms":184,"probe_ms":96,"resume":""}
{"type":"progress","state":"downloading","downloaded":222732288,"total":358411469,"percent":62,"speed":19503514,"connections":16,"eta_ms":7020}
{"type":"completed","url":"https://example.com/image.iso","file":"/tmp/image.iso","bytes":358411469,"sha256":"0f3a…"}
{"type":"summary","total":1,"failed":0}
shell
java -jar turbodl-cli-all.jar <url> -o out.bin -c 16
java -jar turbodl-cli-all.jar <url> --json
java -jar turbodl-cli-all.jar --batch tasks.txt

第一次下载之前会问的事

它需要 Android 吗?

不需要。它是纯 Kotlin/JVM 库。Android 只是使用者之一,服务端或桌面工具同样可以用。唯一要求是 JVM 17+。

必须用插件框架吗?

不必。turbodl-core 不依赖插件框架。需要 HTTP 之外的协议时,框架才有意义。

能下 HLS 吗?

点播可以,通过官方插件:解析清单、分片并发、AES-128 解密。直播清单、DRM 与 fMP4 会被明确拒绝。

有图形界面吗?

这个项目不提供。SDK 有意不画界面。提供的是引擎、插件框架与命令行工具。

从哪里拿文件?

每个文件都是 v0.4.1.0 Release 的附件:自包含 CLI JAR 与离线 Maven 包。目前还没有发布到 Maven Central。

服务端对 Range 撒谎会怎样?

会核对实际返回的字节与请求的那段。忽略 Range 的服务端会被识别,该分片判为失败,而不是写入错位数据。

从一个坐标开始

把引擎加进构建,把字节交给运行时:

还没有发布到 Maven Central。官方支持的用法是 Release 页上的离线 Maven 包,下载页写了具体步骤。