Skip to content

HTTP Range 分段下载

ACE Framework 内置 rangeHandler() 中间件,自动处理标准 HTTP Range 请求(RFC 7233),无需任何配置即可支持:

  • 断点续传(大文件下载)
  • 分段媒体播放(<video> / <audio> 标签拖动进度条)
  • 支持客户端按字节范围获取部分内容

开箱即用

AceApplication.buildApp() 已内置 rangeHandler() 并置于洋葱外层,所有 GET 请求自动支持 Range:

GET /files/video.mp4
< Accept-Ranges: bytes          ← rangeHandler 自动声明
< Content-Length: 1048576

GET /files/video.mp4
Range: bytes=0-65535            ← 客户端带 Range 头
< 206 Partial Content
< Content-Range: bytes 0-65535/1048576
< Content-Length: 65536

不需要任何额外代码,只要响应体是字节数据(文件服务、BytesBody 响应)即可生效。

工作原理

rangeHandler() 是一个后置中间件(置于洋葱最外层):

请求进入

rangeHandler 调 next() ← 先让内层处理

内层返回 200 响应体

rangeHandler 检查 Range 头
   ├─ 无 Range 头     → 加 Accept-Ranges: bytes,保持 200
   ├─ 合法 Range 头   → 切片响应体,改 206 + Content-Range
   └─ 不可满足范围   → 416 + Content-Range: bytes */total

支持的 Range 格式:

格式含义
bytes=0-1023前 1024 字节
bytes=1024-从第 1024 字节到末尾
bytes=-512末尾 512 字节

多段 Range 暂不支持

bytes=0-100,200-300 这类多段请求会被忽略(保持 200 全量响应),符合 RFC 7233 对不支持多段的处理规范。

静态文件 + Range

与内置静态文件中间件组合,直接支持媒体断点续传:

cangjie
import ace_framework_boot.*
import ace_framework_runtime.*

// 只需注册静态文件目录,Range 自动生效
@Middleware[300]
public func staticFiles(): Middleware {
    serveStatic("/files", "./public/files")
}

main(): Int64 {
    AceApplication.run()
    return 0
}

HTML:

html
<video controls>
  <source src="/files/intro.mp4" type="video/mp4">
</video>

浏览器拖动进度条时会自动发送 Range 请求,框架返回 206 分段。

手动控制 Range

绝大多数场景无需手动处理。若需精确控制(如流式读取超大文件避免全部载入内存),可跳过 rangeHandler 自行实现:

cangjie
@Get["/stream/:file"]
public func streamFile(ctx: Context, file: String): Unit {
    let path = "./data/${file}"
    let rangeHeader = ctx.header("range") ?? ""

    // 自行解析 Range 并流式读取,不依赖内置 rangeHandler
    if (rangeHeader.startsWith("bytes=")) {
        // 解析 start/end,以 StreamBody 分段推送
        ctx.status = 206
        ctx.setHeader("Accept-Ranges", "bytes")
        // ... 自定义流式实现
    } else {
        ctx.setBodyBytes(readFileBytes(path))
    }
}

内存限制

内置 rangeHandler 基于 BytesBody,响应体须完整载入内存后切片。超过数百 MB 的文件建议使用 StreamBody 自行实现分段读取,避免 OOM。

响应状态码参考

状态码含义
200 OK无 Range 头,全量响应
206 Partial ContentRange 请求成功,返回指定字节段
416 Range Not SatisfiableRange 范围超出文件大小或格式非法

基于 Apache-2.0 许可证发布