Files
hi-server/doc/tapi-file-upload-zh.md
T
shanshanzhong147 f452f80100
Build docker and publish / build (20.15.1) (push) Failing after 9m55s
Build docker and publish / build (20.15.1) (pull_request) Successful in 8m7s
配置(#98): 扩展文件上传 Content-Type 白名单支持图片
- 在 etc/ppanel.yaml 与 internal/config/config.go 的 S3.AllowedContentTypes 新增 image/jpeg,image/jpg,image/png,image/webp,image/gif,image/heic,image/heif,image/bmp
- 保留原 zip/gzip/text/json/octet-stream
- validateInitRequest 在拒绝时携带 content_type is not allowed 业务消息
- 新增 internal/logic/public/file/common_test.go,覆盖允许/拒绝及无 Content-Type 嗅探
- doc/tapi-file-upload-zh.md 同步允许类型列表与错误码说明

Co-authored-by: multica-agent <github@multica.ai>
2026-05-27 19:50:51 -07:00

5.4 KiB
Raw Blame History

TAPI 文件上传接入说明

本文档说明 https://tapi.hifast.biz/v1/public/file/upload 相关上传接口的推荐接入方式、签名规则与常见排查方式。

总览

上传能力包含两类接入方式:

  • 推荐方式:init -> S3 PUT -> complete
  • 兼容方式:/upload multipart 直传

推荐优先使用预签名三段式,因为:

  • 现有签名串包含 BODY_SHA256
  • /uploadmultipart/form-data
  • multipart 原始 body 的签名和调试成本更高
  • initcomplete 是 JSON,更适合客户端和 Apifox 调试

签名生效逻辑

项目保持现有旧逻辑,不做强制签名改造:

  • Signature.EnableSignature = false 时:不校验签名
  • Signature.EnableSignature = true 且未携带 X-App-Id 时:不校验签名,兼容老客户端
  • Signature.EnableSignature = true 且携带 X-App-Id 时:必须同时携带并校验
    • X-Timestamp
    • X-Nonce
    • X-Signature

这意味着:

  • 新客户端建议始终带完整签名头
  • 老客户端如果没有 X-App-Id,仍可按旧逻辑访问

签名头定义

  • X-App-Id: 客户端标识,例如 ios-client
  • X-Timestamp: Unix 秒级时间戳
  • X-Nonce: 每次请求唯一随机串
  • X-Signature: HMAC-SHA256 结果的十六进制小写字符串

StringToSign 规则

StringToSign 由下面 7 段按换行符 \n 拼接:

METHOD
PATH
CANONICAL_QUERY
BODY_SHA256
X-App-Id
X-Timestamp
X-Nonce

说明:

  • METHODHTTP 方法大写,例如 POST
  • PATH:请求路径,例如 /v1/public/file/upload/init
  • CANONICAL_QUERY:按 key 排序后的 query string,没有 query 则为空字符串
  • BODY_SHA256:请求体原始字节的 SHA-256 十六进制小写
  • 其余三项直接使用请求头值

签名计算方式:

signature = hex_lower(HMAC_SHA256(app_secret, string_to_sign))

时间窗与防重放:

  • X-Timestamp 默认有效时间窗是 300 秒
  • X-Nonce 在有效时间窗内不能重复使用

推荐接入:预签名三段式

1. 初始化上传

请求:

curl -X POST 'https://tapi.hifast.biz/v1/public/file/upload/init' \
  -H 'Accept: application/json, text/plain, */*' \
  -H 'Content-Type: application/json' \
  -H 'authorization: your-token' \
  -H 'X-App-Id: ios-client' \
  -H 'X-Timestamp: 1778776400' \
  -H 'X-Nonce: nonce-001' \
  -H 'X-Signature: your-signature' \
  -d '{
    "biz_type": "app-package",
    "file_name": "demo.zip",
    "content_type": "application/zip",
    "size": 123456,
    "sha256": ""
  }'

典型返回:

{
  "code": 200,
  "msg": "success",
  "data": {
    "file_id": "c29274ee26ab5aa211e0396e",
    "object_key": "app-upload/app-package/519/2026/05/c29274ee26ab5aa211e0396e_demo.zip",
    "upload_url": "https://bucket.s3.ap-east-1.amazonaws.com/...",
    "method": "PUT",
    "headers": {
      "Content-Type": "application/zip"
    },
    "expired_at": 1778776715
  }
}

2. 直传 S3

这一步是直接上传二进制文件到 S3,不走业务签名中间件。

curl -X PUT 'https://bucket.s3.ap-east-1.amazonaws.com/...' \
  -H 'Content-Type: application/zip' \
  --upload-file '/tmp/demo.zip'

说明:

  • Content-Type 需和 init 返回的 headers.Content-Type 一致
  • 允许的 Content-Type 由服务端 S3.AllowedContentTypes 配置控制,默认包含:
    • 压缩包:application/zipapplication/x-zip-compressedapplication/gzipapplication/x-gzip
    • 通用文件:application/octet-streamtext/plainapplication/json
    • 图片:image/jpegimage/jpgimage/pngimage/webpimage/gifimage/heicimage/heifimage/bmp
  • upload_url 有过期时间,通常 300 秒
  • 成功时 S3 常见返回 200204

3. 完成上传

curl -X POST 'https://tapi.hifast.biz/v1/public/file/upload/complete' \
  -H 'Accept: application/json, text/plain, */*' \
  -H 'Content-Type: application/json' \
  -H 'authorization: your-token' \
  -H 'X-App-Id: ios-client' \
  -H 'X-Timestamp: 1778776405' \
  -H 'X-Nonce: nonce-002' \
  -H 'X-Signature: your-signature' \
  -d '{
    "file_id": "c29274ee26ab5aa211e0396e"
  }'

兼容接入:单接口 multipart 直传

接口:

  • POST /v1/public/file/upload

表单字段:

  • biz_type
  • file

说明:

  • 该接口继续保留,兼容旧客户端
  • 如果请求带了 X-App-Id,就按现有逻辑验签
  • 如果没有 X-App-Id,仍按旧逻辑放行
  • 如果要给该接口加签,签名时必须对原始 multipart body 计算 BODY_SHA256
  • 允许的 Content-Type 与预签名三段式一致;multipart 文件字段未显式携带 Content-Type 时,服务端会基于文件内容嗅探常见类型。

常见错误码

  • 200: 成功
  • 400: 参数错误
  • 400 content_type is not allowed: 文件 Content-Type 不在 S3.AllowedContentTypes 白名单内
  • 40008: 缺少签名头
  • 40009: 签名已过期
  • 40010: 签名无效
  • 40011: nonce 重放
  • 10001: 上传元数据不存在或对象不存在

排查建议

  • 40008:确认带了 X-App-Id 后,也同时带上 X-Timestamp / X-Nonce / X-Signature
  • 40009:检查客户端时间是否偏差过大
  • 40010:确认 PATH、query 排序、body 原始字节、secret 是否完全一致
  • 40011:确保每次请求都生成新的 X-Nonce
  • complete 失败:确认 S3 PUT 已成功,且上传大小与 init.size 一致