# TAPI 文件上传接入说明 本文档说明 `https://tapi.hifast.biz/v1/public/file/upload` 相关上传接口的推荐接入方式、签名规则与常见排查方式。 ## 总览 上传能力包含两类接入方式: - 推荐方式:`init -> S3 PUT -> complete` - 兼容方式:`/upload` multipart 直传 推荐优先使用预签名三段式,因为: - 现有签名串包含 `BODY_SHA256` - `/upload` 是 `multipart/form-data` - multipart 原始 body 的签名和调试成本更高 - `init` 和 `complete` 是 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` 拼接: ```text METHOD PATH CANONICAL_QUERY BODY_SHA256 X-App-Id X-Timestamp X-Nonce ``` 说明: - `METHOD`:HTTP 方法大写,例如 `POST` - `PATH`:请求路径,例如 `/v1/public/file/upload/init` - `CANONICAL_QUERY`:按 key 排序后的 query string,没有 query 则为空字符串 - `BODY_SHA256`:请求体原始字节的 SHA-256 十六进制小写 - 其余三项直接使用请求头值 签名计算方式: ```text signature = hex_lower(HMAC_SHA256(app_secret, string_to_sign)) ``` 时间窗与防重放: - `X-Timestamp` 默认有效时间窗是 300 秒 - `X-Nonce` 在有效时间窗内不能重复使用 ## 推荐接入:预签名三段式 ### 1. 初始化上传 请求: ```bash 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": "" }' ``` 典型返回: ```json { "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,不走业务签名中间件。 ```bash 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` 一致 - `upload_url` 有过期时间,通常 300 秒 - 成功时 S3 常见返回 `200` 或 `204` ### 3. 完成上传 ```bash 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` ## 常见错误码 - `200`: 成功 - `400`: 参数错误 - `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` 一致