跳转到内容
HexaTransfer
返回博客
技术深度解析

文件传输API设计:RESTful最佳实践

遵循RESTful最佳实践设计健壮的文件传输API。涵盖身份验证、速率限制、分片上传和错误处理。

设计良好的文件传输 REST API 暴露五六个资源(sessions、parts、shares、downloads、revocations),诚实使用 HTTP 语义(POST 创建、PUT 用于幂等分块、DELETE 用于撤销),并将实际字节传输推给预签名存储 URL,让你的服务器永远不成为带宽瓶颈。认证使用 TLS 1.3 上的短期 bearer token,限速区分创建请求与元数据读取,分块上传遵循 tus 断点续传模式或 S3 分段上传语义,错误遵循 RFC 7807 Problem Details,让客户端能以编程方式处理。

围绕动作而非文件建模资源

常见错误是把 API 建模成文件树。文件传输服务更适合用三种资源建模:

  • /sessions — 进行中的上传,由 POST 创建,通过分块 PUT 填充
  • /shares — 已完成的、可寻址的传输,带有效期和下载配额
  • /downloads — 短期的、已签名的字节访问句柄

session 通过 complete 动作变成 share;share 因时间或下载配额到期而失效。撤销是对 share 的 PATCH 或带撤销 token 的 DELETE。不为"文件"或"文件夹"定义名词——那是存储的实现细节,不是公共合约。

这种设计保持 API 端点数量少(10个以下),HTTP 缓存映射清晰(share 用 Cache-Control: private, max-age=60;download 用 no-store),且能在不破坏客户端的情况下更换存储后端。

认证与授权模式

用户侧 API 颁发短期 JWT(15分钟 TTL),用 EdDSA 签名,通过 HttpOnly Cookie 刷新。token 放在 Authorization: Bearer,绝不放在查询字符串里——那会被记录到日志。

机器间通信,HMAC-SHA256 请求签名优于 bearer token,因为它无需传输密钥即可证明密钥持有。AWS SigV4 是参考设计:

Authorization: HEX4-HMAC-SHA256 
  Credential=AKIA.../20261202/eu/transfer/hex4_request,
  SignedHeaders=host;x-hex-date;x-hex-content-sha256,
  Signature=...

预签名下载 URL 的 TTL 应以分钟计,而非小时。15分钟窗口在可用性与泄露 URL 被转发的风险之间取得平衡。仅当你能接受破坏 CGNAT 后面用户时才加 IP 锁定——通常不能。

分块上传与分段语义

断点续传分块上传有两个可信协议:tus(IETF 草案,Upload-OffsetUpload-Length 头)和 S3 分段(PartNumberUploadIdETag)。选一个并坚持。自己发明协议看起来诱人,但当你意识到需要处理部分写入、乱序分块和废弃 session 时就会后悔。

tus 模式的 REST 形式:

POST   /sessions              -> 201, Location: /sessions/abc
HEAD   /sessions/abc          -> 200, Upload-Offset: 104857600
PATCH  /sessions/abc          -> 204, body = next chunk
POST   /sessions/abc/complete -> 201, Location: /shares/xyz
DELETE /sessions/abc          -> 204

分块大小范围:最小5MB(与 S3 分段保持一致),最大100MB(控制重试成本),默认8MB。分块偏移量与声明的不符时,返回 HTTP 409 Conflict 加说明预期偏移量的 Problem 文档。

反映真实滥用的限速

限速需要按端点成本区分:

  • POST /sessions:每 IP 每小时20次(开销大——分配存储)
  • PATCH /sessions/:id:每 session 每小时10,000次(开销小——写字节)
  • GET /shares/:id:每 IP 每小时1,000次(元数据查询)
  • GET /shares/:id/download:每 IP 每小时100次(出口成本)

在边缘(Cloudflare、Fastly)应用限速作为基础防护,在应用层(express-rate-limit、Fastify 的 @fastify/rate-limit)做第二层突发控制。返回429并附带秒数单位的 Retry-After。成功响应也包含限速头:

RateLimit-Limit: 20
RateLimit-Remaining: 17
RateLimit-Reset: 2843

遵循 IETF RateLimit 草案让行为良好的客户端能主动调速,而不是被阻断。

客户端可操作的错误响应

RFC 7807 Problem Details 是标准。每个错误返回 Content-Type: application/problem+json

{
  "type": "https://api.example.com/errors/chunk-offset-mismatch",
  "title": "Chunk offset mismatch",
  "status": 409,
  "detail": "Server expected offset 5242880, received 4194304.",
  "expected_offset": 5242880,
  "session_id": "abc123"
}

type URI 应有文档且稳定——客户端靠它做模式匹配。title 是通用描述;detail 是具体说明。自定义字段添加机器可操作的上下文。错误中绝不泄露堆栈跟踪、文件路径或内部 ID。

HTTP 状态码诚实映射:400 格式错误输入,401 缺少认证,403 已认证但无权限,404 仅用于资源从未存在过(过期的 share 用410 Gone),413 超配额载荷,429 限速,500 程序错误,503 维护期。

内容协商与流式传输

上传端点应接受 application/octet-stream 并要求 Content-Length。分块上传拒绝 multipart/form-data——增加解析开销,没有任何收益。接受 Content-Range 用于 tus 风格的部分写入。

下载端点应支持 HTTP Range 请求(RFC 7233)以实现断点续传:

GET /shares/xyz/blob
Range: bytes=104857600-209715199
-> 206 Partial Content
   Content-Range: bytes 104857600-209715199/2147483648

这是让浏览器在 Wi-Fi 中断后恢复2GB下载的机制。大多数 S3 兼容存储原生支持 Range 请求——你的 API 只需转发或预签名。

版本管理而不积累技术债

通过 URL 路径版本(/v1/sessions),而非请求头。路径版本在日志中可见、可缓存,比 Accept: application/vnd.example.v1+json 更易调试。v2 发布后至少保持 v1 支持24个月。自由添加字段(客户端必须忽略未知字段);在稳定版本中永远不删除或重命名字段。

需要破坏性变更时,v1 和 v2 并行运行12个月,在 v1 响应上附加 Sunset 头(RFC 8594),并发布带真实前后对比示例的迁移指南。

可观测性与可调试性

每个响应应包含从请求中回显或自动生成的关联 ID(X-Request-ID)。以结构化 JSON 记录 ID、客户端 IP(如对隐私敏感则哈希处理)、端点、状态和耗时。不记录请求体——这是加密密钥最终出现在日志平台里的方式。

每个端点发出指标:请求数、p50/p95/p99 延迟、错误率、入站字节、出站字节。对 p99 延迟尖峰和超过基线的5xx率发出告警。通过 OpenTelemetry 进行追踪,获取跨 API、存储和数据库的请求流。

HexaTransfer 的 API 遵循上述模式——资源精简、tus 风格分块上传、Problem Details 错误、15分钟预签名下载、RateLimit 响应头。免费试用 hexatransfer.com——无需注册,单次最大10GB。

与现实一致的文档

将 OpenAPI 3.1 规范与 API 一同发布,并与服务端代码放在同一仓库,让 schema 漂移成为 PR 审查问题而非生产故障。从规范生成至少一个 SDK(TypeScript 或 Python)并在自己的示例中使用——SDK 的 bug 会快速暴露规范的 bug。每个端点包含 curl 示例,一个20行以内完成真实文件上传的快速开始指南,以及专门记录每个错误 type URI 的页面。开发者能在一个下午集成的 API,最终会被集成到比需要一周逆向工程的 API 多10倍的产品中。

通过端到端加密安全发送大文件

通过端到端加密免费传输最大10GB的文件。无需注册账户。文件在上传前在浏览器中加密,其他人无法读取。

发送文件