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 Content | Range 请求成功,返回指定字节段 |
416 Range Not Satisfiable | Range 范围超出文件大小或格式非法 |