本文是一篇实录型教程:作者用自己的账号(昵称"桂棹碧波")创建了一个真实应用「交付机器人」,用真实文件、真实调用了文档里的每一个接口,所有截图和响应报文都来自这次真实的接入过程,没有摆拍,也没有任何推销话术。它能回答三个问题:
1. 怎么用代码把一支 28MB 的视频发给客户,生成客户打开就能下载的链接?
2. 怎么把文件直传进文件库和项目——文件字节不经过应用服务器,还支持断点续传?
3. 传完之后,交付链接、下载回执、积分账单怎么对账,每一笔消耗归谁,看得见吗?
写在前面:这篇教程的几点说明
- 演示账号:作者自己的测试账号(昵称"桂棹碧波",手机号 137****6510,已开通个人套餐)。
- 演示环境:截图取自本地部署的测试环境,部分截图中链接显示为
localhost:18099开头。实际接入时,把接口域名换成冰梭正式站点即可,接口与流程完全一致。 - 演示应用:在开放平台创建的应用「交付机器人」,AppKey 为
ak_ba8a55b6adc74b27b70cc4f0。AppSecret 属于敏感凭证,文中与截图均已打码——你的 Secret 只在创建时完整显示一次,请妥善保管。 - 演示文件:一支 28MB 的视频《春夏季新品宣传片_初剪.mp4》、一个 9MB 的素材压缩包《外景拍摄素材_上午场.zip》、一份 679 字节的《品牌授权书_签署版.pdf》,均为作者自造的测试文件。
- 接入语言:文中的调用示例用 Node.js 写成(
fetch原生可用),但开放平台是纯 HTTP 协议,curl、Python、Java、C# 任何能发 HTTP 请求的语言都能接入;浏览器环境推荐直接用官方 JS SDK(第八节)。 - 每一步的真实响应报文,作者都落盘留档了(下文代码块里的 JSON 均为原样摘录,超长的预签名 URL 以
……截断)。
一、开放平台长什么样:门户与接口文档
未登录也能浏览开放平台门户和接口文档。门户入口在官网顶部导航的「开放」:
接口文档覆盖传输、文件库、项目交付、计费等全部开放能力,左侧是目录树,点击切换右侧内容,每个接口都给出请求方法、路径、参数和输入输出格式:
先建立一个整体认知——开放平台把「大文件传输」拆成了四种业务形态,后文按场景逐一实测:
| 形态 | 适合干什么 | 文件字节走哪 |
|---|---|---|
| 安全中转(relay) | 给客户发一支片子,生成取件链接 | 浏览器/程序 → 对象存储,服务器只发预签名 URL |
| P2P 直传(p2p) | 双方都在线,实时点对点 | 双方浏览器直连,不落盘 |
| 分块直传(upload-sessions) | 把文件传进你的文件库 / 项目 / 收件箱 | 客户端直连对象存储,应用服务器不碰文件 |
| 交付(delivery) | 把项目里的文件打包成一条交付链接给客户确认 | 复用项目文件,无需重新上传 |
二、创建应用:拿到 AppKey 与 AppSecret
登录后进入「应用管理」,这里是所有开放平台应用的入口:
点击「创建应用」,填写名称和描述。描述建议写清楚用途,方便以后审计:
创建成功后,系统一次性展示 AppKey 和 AppSecret。Secret 只显示这一次,关掉弹窗就再也查不到了(只能重置)。下面的截图里 Secret 已经打码:
关闭弹窗,应用列表里出现了「交付机器人」:
点开应用详情,可以看到调用统计和调用日志——每次 API 调用都有留痕,接入了多少次、什么时候调的、调的哪个接口,一目了然:
三、签名鉴权:五个请求头,一次算对
所有开放接口都要求携带认证头。服务端验证签名与时间窗后,以「应用创建者本人」的身份执行请求——和页面登录同权。其中四个必填,X-Algorithm可选(不传时服务端按 SM3 国密口径校验)。
| 请求头 | 必填 | 说明 |
|---|---|---|
X-App-Key | 是 | 应用的 AppKey |
X-Timestamp | 是 | Unix 秒级时间戳,与服务器时差超过±300 秒即拒绝 |
X-Nonce | 是 | 随机串,参与签名串,确保同一时间戳内每次请求的签名各不相同 |
X-Algorithm | 可选 | 摘要算法:SM3(国密,缺省值)或SHA-256,用于请求体哈希与签名 |
X-Signature | 是 | HMAC 签名,十六进制小写 |
3.1 签名怎么算
三步:
- 请求体哈希:
bodyHash = SHA-256(body)(声明 SM3 时则为 SM3 哈希;本文示例统一声明 SHA-256,不声明时服务端按 SM3 校验),十六进制小写;GET 请求或空请求体时为空字符串。 - 构造签名串:把
appKey + timestamp + nonce + METHOD + path + bodyHash裸拼接(无分隔符、无换行)。METHOD 大写;path 只取请求路径(如/api/open/relay/create),不含域名、不含 query 参数。 - 计算签名:
X-Signature = HMAC-SHA256(appSecret, 签名串)(X-Algorithm 为 SM3 时用 HMAC-SM3)。
作者实测用的 Node.js 签名函数(后续所有场景都基于它):
import crypto from 'crypto'; const APP_KEY = 'ak_ba8a55b6adc74b27b70cc4f0'; const APP_SECRET = '9c1b****af5c'; // 你的 AppSecret,切勿写进前端代码 const API = 'https://www.wringcloud.com'; // 实际接入时替换为正式站点 const sha256hex = b => crypto.createHash('sha256').update(b).digest('hex'); function authHeaders(method, path, body) { const ts = Math.floor(Date.now() / 1000).toString(); const nc = crypto.randomBytes(8).toString('hex'); // Nonce:每次随机 const bodyHash = body && body.length ? sha256hex(body) : ''; // GET/空体为空串 const signString = APP_KEY + ts + nc + method.toUpperCase() + path + bodyHash; const sign = crypto.createHmac('sha256', APP_SECRET).update(signString).digest('hex'); return { 'X-App-Key': APP_KEY, 'X-Timestamp': ts, 'X-Nonce': nc, 'X-Signature': sign, 'X-Algorithm': 'SHA-256' }; }两个容易踩的坑,都是作者实测验证过的:
1.签名串里只放路径,不放 query 参数。比如
/api/open/delivery/list?projectId=37,参与签名的是/api/open/delivery/list。2.本机时钟要准。时间戳窗口只有 ±300 秒,系统时间漂移会直接 401。
3.2 统一响应格式
所有接口统一返回{"code":num,"data":...,"message":"..."}:业务数据在data,错误说明在message,code同时反映在 HTTP 状态码里。
3.3 真实报错长什么样
作者故意发了两次错误请求,留了真实报文:
不带任何认证头调用/api/open/billing/points:
HTTP 401 { "code": 401, "data": null, "message": "缺少认证参数" }用错误的 Secret 计算签名:
HTTP 401 { "code": 401, "data": null, "message": "签名验证失败" }四、场景一:安全中转——用 API 给客户发一支 28MB 视频
这是最常用的场景:程序自动创建一条中转任务,拿到取件链接发给客户,客户打开链接就能下载,不需要注册。
4.1 创建中转任务
文档页的「创建中转任务」接口页:
调用POST /api/open/relay/create,声明文件名、大小和取件策略:
{ "fileName": "春夏季新品宣传片_初剪.mp4", "fileSize": 29360128, "maxReceiveCount": 3, "expireHours": 72, "isOffline": true }真实响应:
{ "code": 200, "message": "success", "data": { "transferId": "depgGWrjKR", "receiveToken": "46d33dc9bacc44d38524efd71866ad30", "chunkSize": 4194304, "firstChunkSize": 1048576, "totalChunks": 8, "expiresIn": 259200, "downloadLink": "https://www.wringcloud.com/r/depgGWrjKR?token=46d33dc9……", "preAuthId": "f0dc817190ee408ab10cffe118aca1e3" } }注意三点:
- 分块策略:首块 1MB,之后每块 4MB,28MB 被切成 8 块;
expiresIn: 259200:任务有效期 72 小时,和请求里的expireHours一致;maxReceiveCount: 3:这个链接最多被下载 3 次,防止外泄后被反复下载。
4.2 分块上传:拿凭证 → 直传对象存储 → 回执
每一块都是同一个循环:签发凭证 → PUT 对象存储 → 确认回执。
第一步,POST /api/open/relay/{transferId}/chunk-upload-url请求某一块的上传凭证,服务端返回一个预签名 URL(有效期 3600 秒):
{ "code": 200, "message": "success", "data": { "uploadUrl": "https://wringcloud-bs.oss-cn-hangzhou.aliyuncs.com/oss_relay/20261006/27300124_…….mp4?Expires=1791282351&OSSAccessKeyId=LTAI5tA……&Signature=Qp%2B0kyL8pn019WytWxIbhP5TfaU%3D&uploadId=0C5199AB……&partNumber=1", "chunkSize": 1048576, "expiresIn": 3600 } }第二步,拿着这个 URL 把分块字节PUT到对象存储——文件字节直接进对象存储,不经过应用服务器:
const cred = (await call('POST', `/api/open/relay/${transferId}/chunk-upload-url`, { chunkIndex: i })).data; const chunk = buf.subarray(offset, offset + cred.chunkSize); await fetch(cred.uploadUrl, { method: 'PUT', body: chunk });第三步,POST /api/open/relay/{transferId}/chunk-uploaded确认这一块已落地:
{ "code": 200, "message": "success", "data": { "chunkIndex": 0 } }8 块循环完成后,POST /api/open/relay/{transferId}/complete通知服务端合并:
{ "code": 200, "message": "success", "data": { "transferId": "depgGWrjKR", "message": "上传完成", "status": 2 } }4.3 查询任务与生成下载地址
GET /api/open/relay/{transferId}查询任务状态:
{ "code": 200, "message": "success", "data": { "transferId": "depgGWrjKR", "fileName": "春夏季新品宣传片_初剪.mp4", "fileSize": 29360128, "transferMode": "oss_relay", "status": 2, "maxReceiveCount": 3, "receiveCount": 0, "createdAt": "2026-10-06T17:25:51", "expiredAt": "2026-10-09T17:25:51" } }GET /api/open/transfer/{transferId}/download-url?token={receiveToken}生成整文件下载地址(也可以直接把创建时返回的downloadLink发给客户,效果相同):
{ "code": 200, "message": "success", "data": { "downloadUrl": "https://www.wringcloud.com/api/oss-relay/open-file/depgGWrjKR?token=46d33dc9……", "expiresInSeconds": 300, "billingNote": "链接被访问时按整文件大小扣发送方下载流量,每次访问计费一次" } }billingNote把计费口径说得很直白:客户每打开一次链接,按整文件大小扣一次发送方的下载流量。
4.4 客户视角:打开链接就能下载
把链接发给客户(作者用无痕浏览器模拟)。客户打开链接,不需要注册、不需要装任何软件,页面上标注了文件信息和剩余接收次数:
点「下载文件」,浏览器分块下载:
作者把下载到的文件和原始文件做了逐字节比对:29,360,128 字节,完全一致。
4.5 发送方视角:传输记录有留痕
回到页面端「传输记录」,这条 API 创建的任务和手动发送的任务并列出现,模式、状态、过期时间都可见:
五、场景二:分块直传——文件不经过应用服务器,还支持断点续传
第四节的"分块上传"是给客户发文件;这一节是把文件传进自己的文件库/项目。这是冰梭 v2.1.0 直传协议的主场:客户端凭预签名 URL 直连对象存储,应用服务器从头到尾不接触文件字节。无论你用服务器脚本、浏览器还是别的任何 HTTP 客户端,协议完全一致。
接口文档的「文件库域」页,顶部蓝色提示框写明了直传协议的五步:
5.1 直传协议全景
整个流程五步,一张时序图说清:
- 初始化会话:
POST /api/open/upload-sessions,声明业务类型(drive=文件库 /project=项目 /collect=收件箱)和文件指纹; - 批量签发凭证:
POST /api/open/upload-sessions/{sessionKey}/credentials,一次最多签 64 块; - 直传对象存储:凭预签名 URL 逐块
PUT; - 逐块回执:
POST /api/open/upload-sessions/{sessionKey}/chunks/{index}/ack; - 合并落地:
POST /api/open/upload-sessions/{sessionKey}/complete。
5.2 实测:直传 9MB 素材包进文件库
先在文件库建一个归档文件夹(POST /api/open/drive/folders),返回folderId: 11。然后初始化直传会话:
{ "bizType": "drive", "bizRef": { "folderId": 11 }, "fileName": "外景拍摄素材_上午场.zip", "fileSize": 9437184, "fileHash": "20ff1481……(文件的 SHA-256,十六进制)" }真实响应:
{ "code": 200, "message": "success", "data": { "sessionKey": "c3c9105a59d74174a2c4f0f27f82cbdc", "bizType": "drive", "fileName": "外景拍摄素材_上午场.zip", "fileSize": 9437184, "chunkSize": 8388608, "totalChunks": 2, "uploadedChunks": [], "resumed": false } }直传的块比中转更大:每块 8MB,9MB 文件共 2 块。接着一次签发两块凭证:
{ "code": 200, "message": "success", "data": { "credentials": [ { "chunkIndex": 0, "uploadUrl": "https://wringcloud-bs.oss-cn-hangzhou.aliyuncs.com/drive/20261006/…….zip?Expires=1791282629&……&partNumber=1", "expiresIn": 3600 }, { "chunkIndex": 1, "uploadUrl": "https://wringcloud-bs.oss-cn-hangzhou.aliyuncs.com/drive/20261006/…….zip?Expires=1791282629&……&partNumber=2", "expiresIn": 3600 } ] } }逐块PUT→ 逐块ack→complete合并落地:
{ "code": 200, "message": "success", "data": { "fileName": "外景拍摄素材_上午场.zip", "fileSize": 9437184, "transferId": "f744fd81245d4a2e83e93e8d5df7cbdf" } }落地后用GET /api/open/drive/files/{transferId}/download-url拿下载地址,把文件拉回来逐字节比对:9,437,184 字节,完全一致。打开页面端「我的文件库」,新文件夹和文件都在:
5.3 直传协议的三个内建机制(实测验证)
- 断点续传:初始化会话时,服务端按「同一用户 + 相同文件哈希」匹配未完成的会话,响应里的
uploadedChunks会列出已上传的块、resumed标记为true,凭据签发时这些块直接跳过。传到一半断网,重跑同一份文件即可续传,不用从头再来。 - 会话与应用隔离:直传会话归属于创建它的应用,用应用 A 的密钥去操作应用 B 创建的会话,服务端直接返回 403。作者在 SDK 黑盒测试里专门验证过这一条。
- 凭证限时:预签名 URL 有效期 3600 秒,过期重签即可;批量签发一次最多 64 块,超大文件按批循环。
六、场景三:项目交付——API 全自动"传文件进项目 + 发交付链接"
这个场景把前两节拼起来:文件直传进项目 → 创建交付链接 → 客户凭提取码取件 → 拿交付回执。这是把"给客户交片"整段流水线自动化的核心。
6.1 项目与文件
作者提前在页面上建好了演示项目「自动化交付演示」(projectId: 37)。用GET /api/open/projects/37查详情、GET /api/open/projects列出全部项目,然后直传一份《品牌授权书_签署版.pdf》进项目——和文件库是同一套五步直传协议,只是bizType换成project:
{ "bizType": "project", "bizRef": { "projectId": 37 }, "fileName": "品牌授权书_签署版.pdf", "fileSize": 679, "fileHash": "……" }合并落地的响应和文件库不一样,多了项目维度的归属信息:
{ "code": 200, "message": "success", "data": { "transferId": "18652d5b3daf4798881bc4920e1f20b2", "fileId": 831, "projectId": 37, "fileName": "品牌授权书_签署版.pdf", "fileSize": 679 } }GET /api/open/projects/37/files确认文件已入库,uploaderName显示为应用创建者"桂棹碧波":
6.2 重要前提:审核后交付
如果你的空间或项目开启了「审核后交付」,通过 API 上传的文件同样要先审核通过(或被标记为可交付)才能创建交付链接——API 与页面端执行同一套审核判定,不存在绕过审核的后门。页面端的上传与审核流程:
作者的演示项目在项目设置里把「交付内容审核」设为了关闭,所以直传落地的文件可以直接交付。如果你的项目开着审核,请先走完审核,再执行 6.3——创建交付链接时,未过审的文件会被跳过(响应里的skippedCount会告诉你跳过了几个)。
6.3 创建交付链接
POST /api/open/delivery/create,把fileId: 831打包成交付链接,附上提取码、有效期和一段给客户看的交付说明:
{ "projectId": 37, "scope": "files", "taskIds": [831], "password": "bs2026", "expireDays": 7, "note": "初剪版本,请于 7 日内确认;有任何修改意见直接回复本条交付。" }真实响应:
{ "code": 200, "message": "success", "data": { "id": 17, "projectId": 37, "scope": "files", "token": "147a496904b34250b65334c0352e74cd", "password": "bs2026", "expireAt": "2026-10-13T17:30:47", "maxDownloads": 0, "downloadCount": 0, "fileCount": 1, "skippedCount": 0, "note": "初剪版本,请于 7 日内确认;有任何修改意见直接回复本条交付。", "creatorName": "桂棹碧波" } }拼出客户取件链接:https://www.wringcloud.com/delivery.html?token={token}(演示环境为http://localhost:18099/delivery.html?token=147a496904b34250b65334c0352e74cd)。
页面端「交付」标签页里,这条 API 创建的交付链接同步可见,状态"生效中":
6.4 客户取件:提取码 + 下载
客户打开交付链接,输入提取码(bs2026)后进入取件页,看到交付说明信和文件列表:
6.5 交付回执:客户下载了没有,API 可查
GET /api/open/delivery/17查详情,GET /api/open/delivery/list?projectId=37列出项目全部交付链接,GET /api/open/delivery/17/records?page=1&size=10查交付回执——本次演示时客户还没有下载,所以回执列表是空的(total: 0);客户下载后,每一次取件都会生成一条回执记录,谁在什么时候下载了哪个文件,有据可查。
七、场景四:收件收集与 P2P 直传(简述)
7.1 收件箱直传(collect)
客户给你传文件也能走 API:先在页面端「收件管理」生成一条收件链接拿到 token,然后直传协议第五步不变,bizType换成collect,bizRef传入收件令牌和提交人身份:
{ "bizType": "collect", "bizRef": { "token": "收件链接中的token", "uploaderName": "张明" }, "fileName": "外景拍摄素材_下午场.zip", "fileSize": 9437184, "fileHash": "……" }合并落地返回{ fileName, fileSize, autoAccepted }——autoAccepted表示该收件链接开启了自动入库,客户传完直接进收件箱,不需要你手动领取。
7.2 P2P 直传(p2p)
双方都在线时的实时直传:POST /api/open/p2p/create创建任务后,API 返回信令地址、ICE 服务器配置和信令 token,实际的点对点传输由浏览器完成(服务器只做撮合,文件不落盘):
{ "code": 200, "message": "success", "data": { "transferId": "depgJRUIto", "receiveLink": "https://www.wringcloud.com/r/depgJRUIto?token=258cbcbf……", "signalingUrl": "wss://www.wringcloud.com/ws/signaling", "iceServers": [ { "urls": "stun:www.wringcloud.com:3478" }, { "urls": "turn:www.wringcloud.com:3478", "username": "iceshuttle", "credential": "……" } ], "expiresIn": 86400 } }P2P 需要处理 WebRTC 信令,建议直接用官方 JS SDK(下一节),SDK 把信令、ICE、断线重连全部封装好了。
八、用 SDK 接入:iceshuttle-js(推荐)
上面的 HTTP 协议适合任何语言;如果你的运行环境是浏览器,官方 JS SDK 把签名、分块、并发、断点续传全部封装好了,推荐直接用 SDK。
8.1 安装与初始化
SDK 提供三种构建产物:iceshuttle.min.js(生产,script 引入)、iceshuttle.esm.js(ES Module,import 引入)、iceshuttle.umd.js(兼容两种)。下载地址见开放平台文档页「SDK 安装与初始化」,当前版本v2.1.0。
<!-- 方式一:script 标签 --> <script src="./sdk/iceshuttle.min.js"></script> <script> const { IceShuttle, downloadBlob, sha256Hex } = window.IceShuttle; </script>// 方式二:ES Module import { IceShuttle, sha256Hex } from './sdk/iceshuttle.esm.js'; const client = new IceShuttle({ appKey: 'ak_ba8a55b6adc74b27b70cc4f0', // 你的 AppKey appSecret: '9c1b****af5c', // 你的 AppSecret,切勿暴露在前端代码 serverDomain: 'https://www.wringcloud.com', debug: false });8.2 一行代码的大文件直传
v2.1.0 起 SDK 内置了第五节的整套直传协议。高层 APIuploadFile一行完成全部五步(算哈希 → 初始化会话 → 并发上传 → 逐块回执 → 合并落地):
const result = await client.uploadFile(file, { bizType: 'drive', // drive=文件库 / project=项目 / collect=收件箱 bizRef: { folderId: 11 }, // 按 bizType 传入:目录 ID / 项目 ID / 收件 token onProgress: (percent, detail) => console.log(percent + '%') }); // drive 返回 { transferId };project 返回 { transferId, fileId, projectId } // collect 返回 { fileName, fileSize, autoAccepted }需要自己控制并发、断点续传或中途取消时,用低层 API:
const uploader = await client.createUploader({ bizType: 'drive', bizRef: { folderId: 0 }, fileName: file.name, fileSize: file.size, fileHash: await sha256Hex(file), // SDK 直接导出 sha256Hex 工具函数 concurrency: 3 // 并发数 1-8 }); await uploader.upload(file, { onProgress: p => console.log(p + '%') }); await client.completeUploadSession(uploader.sessionKey); // 中途放弃:await uploader.abort(); 服务端清理会话P2P 与安全中转同样有现成方法(P2PSender/P2PReceiver/RelaySender/RelayReceiver/UniversalReceiver),详见接口文档页的 SDK 小节。
8.3 在线 Demo 实测
文档页提供在线 Demo(/sdk/iceshuttle-js/demo/index.html),填入 AppKey/AppSecret 即可在浏览器里体验三种模式。作者用 Demo 的「开放平台直传」tab 真实传了一遍 28MB 的视频:分 4 块、每块 8MB,进度条走到 100%,日志区打出了落地结果{"fileName":"春夏季新品宣传片_初剪.mp4","fileSize":29360128,"transferId":"5b5d5c3e……"}:
九、积分对账:每一笔消耗都归因到应用
开放平台的所有计费动作都记录在案,而且明确区分「谁消耗的」和「消耗的是谁的」。
9.1 API 对账接口
GET /api/open/billing/points查余额总览:
{ "code": 200, "message": "success", "data": { "memberPointsTotal": 1500, "transferRemaining": 750, "pointsBalance": 1.875, "trafficQuotaBytes": 1610612736000, "trafficUsedBytes": 134872039 } }GET /api/open/billing/logs?page=1&size=10查消耗流水。这是作者实测的一条真实记录——注意source: "open_api"和appName: "交付机器人"两个字段:
{ "fileName": "外景拍摄素材_上午场.zip", "source": "open_api", "appName": "交付机器人", "consumeType": "drive_download", "bytes": 9437184, "memberPoints": 0.008, "createdAt": "2026-10-06T17:26:15" }9.2 页面端对账
页面端「积分消耗」页同步展示这些记录,API 产生的消耗带「开放平台 · 交付机器人」角标,与网页手动操作(标"访客")清晰区分:
9.3 计费口径(均为实测)
| 动作 | 谁付费 | 计费方式 | 本次实例 |
|---|---|---|---|
| 直传上传(文件库/项目/收件) | 不计费 | 上传不计费 | — |
| 中转/文件库文件被下载 | 发送方 | 按下载流量,约 1 积分 ≈ 1GB | 9MB 扣 0.008 积分 |
| 交付链接被客户下载 | 发送方 | 同上,按下载流量 | 见积分流水 |
| P2P 实时直传 | 不计费 | 点对点不经过服务器 | — |
三点提醒:
- 积分扣的是应用创建者的。客户下载文件产生的流量从你的积分池扣,客户本身不需要付费、不需要注册。
- API 消耗和页面消耗在同一个池子里,流水里逐条标注了来源(
open_api/web)与应用名,对账时按这两个字段筛选即可。 - 积分不足时接口会直接报错并给出建议(见第十节的 413 样例)。
十、错误处理:真实报错样例与排查清单
10.1 错误码与真实报文
| HTTP | code | message 实例 | 什么时候发生 |
|---|---|---|---|
| 401 | 401 | 缺少认证参数 | 请求没带认证头 |
| 401 | 401 | 签名验证失败 | Secret 不对、签名串拼错、bodyHash 不符,以及时间戳超出 ±300 秒窗口(时间戳过期对外同样报"签名验证失败",服务端日志里才细分"时间戳过期",作者核对过后端实现) |
| 401 | 401 | 无效的AppKey | AppKey 不存在或拼写错误 |
| 403 | 403 | 应用已被禁用/应用已被拉黑:{原因} | 应用状态非"启用"(被手动禁用或违规拉黑) |
| 403 | 403 | 应用隔离拦截 | 用应用 A 的密钥操作应用 B 创建的直传会话 |
| 413 | 413 | 剩余下载积分不足(约1409积分,1积分=1GB下载流量)。可分卷压缩后分次发送、购买加油包(¥1=2积分,永久有效),或开通套餐获得本期积分(个人套餐200/工作室套餐600/团队套餐1500积分/年) | 文件超出剩余可用下载流量(作者传了一个 99TB 的"文件"实测) |
413 这条报错值得点赞:报错信息直接给出了下一步怎么办。同样的思路,建议你的代码也把message透传给用户,而不是只报"上传失败"。
10.2 排查清单
接入调试时,按这个顺序核对(作者实测全部踩过或验证过):
- 401 签名验证失败→ 依次检查:Secret 是否正确、签名串是否裸拼接(无分隔符)、METHOD 是否大写、path 是否不含 query、bodyHash 算法与
X-Algorithm是否一致; - 核对完还是 401→ 校准本机时钟(NTP):时间戳窗口只有 ±300 秒,过期对外统一报"签名验证失败",服务端日志里才细分"时间戳过期";
- 403 应用隔离→ 确认操作会话用的 AppKey 和创建会话时一致;
- 413 流量不足→ 分卷压缩、购买加油包或升级套餐;
- 交付链接创建后文件被跳过→ 检查项目是否开启审核,先审核再交付(见 6.2)。
FAQ
Q:一定要用 JS SDK 吗?后端语言能接入吗?
A:能。开放平台是纯 HTTP 协议,本文第三到七节的全部示例都是 Node.jsfetch直调的,没有依赖任何 SDK。JS SDK 的价值在于浏览器环境里帮你封装了签名、WebRTC 信令和直传并发逻辑。
Q:直传协议支持哪些客户端?
A:任何能发 HTTP 请求的客户端——服务器脚本、浏览器、curl、移动端。文件字节直连对象存储的预签名 URL,应用服务器不碰文件,所以不存在"必须用某种 SDK"的限制。
Q:上传到一半程序崩了怎么办?
A:重新初始化会话即可。服务端按「同一用户 + 相同文件哈希」匹配未完成会话,已上传的块在uploadedChunks里列出,凭据签发自动跳过这些块,只传剩下的部分。
Q:文件会不会被别的应用看到?
A:不会。直传会话与应用绑定,跨应用访问返回 403;数据归属与页面端一致,都挂在你的账号空间下。
Q:支持国密算法吗?
A:支持。X-Algorithm可选SM3(默认)或SHA-256,签名相应使用 HMAC-SM3 或 HMAC-SHA256;JS SDK 初始化时传useSM3: true即可切换。
Q:客户下载需要注册吗?
A:不需要。中转链接、交付链接客户打开即可下载,交付链接可加提取码保护;收件链接客户也无需注册,填提交人身份即可上传。
Q:怎么区分一条积分消耗是 API 产生的还是页面操作产生的?
A:看两个维度:source字段区分来源渠道(open_api/web),appName字段标注具体是哪个应用消耗的。页面端「积分消耗」页有对应的「开放平台 · 应用名」角标。
结语
把整个接入过程串起来,冰梭开放平台解决的是一件事:把"大文件交付"这条链路做成你的系统可以自动调用的能力。
- 发:一个
relay/create加分块循环,28MB 视频变成客户打开就能下载的链接,不用教客户操作; - 传:一套五步直传协议通吃文件库、项目、收件箱三个业务,文件字节不经过应用服务器,断点续传与应用隔离内建;
- 交:文件直传落地 → 一条
delivery/create生成带提取码的交付链接,回执可查,审核门槛与页面端同一套口径; - 管:调用留痕在应用详情、积分归因到
open_api+ 应用名,每一笔消耗都能对上账。
签名只有五个头、一次 HMAC;直传只有五个步骤、一张时序图。对于需要把交付流水线接进自己业务系统的工作室和团队来说,这套东西的上手成本主要在"读文档",而不是"斗协议"。