@yeez-tech/dos-sdk
v0.1.1
Published
Dianshu Object Storage (DOS) SDK — Node.js upload client with COS protocol, pause/resume and checkpoint persistence
Readme
@yeez-tech/dos-sdk
典枢对象存储(DOS)Node.js SDK。封装业务侧 putObject 上传流程:向 DOSBackendService 申请端点 → 按协议直传(当前仅 腾讯云 COS)→ 上报完成,并统一 暂停 / 恢复 与断点续传。
本期独立交付。后续由
dianshu-file-transfer接入本 SDK,再由客户端接入 file-transfer。
运行时:仅 Node(Electron main / 客户端侧),不面向浏览器。
Install
npm i @yeez-tech/dos-sdkPeer / native 依赖说明:
cos-nodejs-sdk-v5— COS 直传better-sqlite3— 上传 checkpoint 持久化(需能编译原生模块)
Quick start
import { createDosClient } from "@yeez-tech/dos-sdk";
const client = createDosClient({
baseUrl: "https://dos.example.com", // DOSBackendService,主域名待定
token: "<jwt>",
clientVersion: "1.0.0",
businessCode: "dianshu-test",
// checkpointDbPath: "/path/to/checkpoints.db", // 默认 cwd/.dos-sdk/checkpoints.db
});
const result = await client.putObject({
objectId: "biz-globally-unique-id",
filePath: "/data/payload.bin",
// businessCode: "dianshu-test", // 可覆盖 client 级默认值
storagePreference: "COS",
onProgress: (p) => console.log(p.percent, p.speed),
});
console.log(result.objectUrl); // dos://...Pause / resume / cancel
await client.pause(objectId);
await client.resume(objectId, { onProgress });
await client.cancel(objectId); // 不再续传;可选 { forget: true } 删除 checkpoint上层只面对统一语义;SDK 内部按 COS 任务是否仍在本进程,自动选择 restartTask 或 uploadFile 续传。
Parallel uploads(同一 DosClient)
同一个 client 上对不同 objectId 并发 putObject 即可,无需再建第二个 SDK:
const [a, b] = await Promise.all([
client.putObject({
objectId: "biz/file-a",
filePath: "/data/a.bin",
onProgress: (p) => console.log("a", p.percent),
}),
client.putObject({
objectId: "biz/file-b",
filePath: "/data/b.bin",
onProgress: (p) => console.log("b", p.percent),
}),
]);
await client.pause("biz/file-a"); // 只暂停 a,b 继续
await client.resume("biz/file-a");内部按 objectId 隔离 COS 会话与 STS,进度 / pause / cancel 都按 objectId 路由。
Events & callbacks
任务回调(putObject / resume)与 Client 事件可同时使用;事件 payload 均带 objectId。
| 事件 | 含义 |
|------|------|
| progress | 字节进度(含 percent / loaded / total / speed) |
| progress-text | 阶段文案(phase + text,不落盘) |
| object-upload-speed | 上传速率 |
| complete | 上传成功 |
| error | 传输终态失败(同时触发 onError;Promise 仍 reject) |
| action-error | pause / resume / cancel / putObject 控制路径失败 |
| stop | 暂停或取消(reason: paused \| canceled) |
import { DosClientEvent } from "@yeez-tech/dos-sdk";
client.on(DosClientEvent.ProgressText, ({ objectId, phase, text }) => {
console.log(objectId, phase, text);
});
client.on(DosClientEvent.Stop, ({ objectId, reason }) => {
console.log(objectId, reason);
});
await client.putObject({
objectId: "biz-1",
filePath: "/data/a.bin",
onProgress: (p) => console.log(p.objectId, p.percent),
onError: (e) => console.error(e.code, e.error),
});Protocol extension
COS 是内置 ProtocolUploader。按 getUploadEndpoint.protocol 选型;可用 registerProtocol() 或构造参数 protocolUploaders 注册 TUS/S3 等适配器,公开 putObject / pause / resume / cancel 不变。
Dynamic STS(上传中刷新凭证)
开始上传后,COS getAuthorization 会 POST parameter.dynamicData.url(getCosTmpCredential,body 带 cosKey)刷新 STS;bucket / region / cosKey 仍用首次 endpoint(checkpoint),不随刷新改 Key。
COS SDK 自身会缓存 STS(剩余有效期 >60s 时不回调),因此不会每个分片都打 DOS。
objectId 语义
| 概念 | 谁提供 | 说明 |
|------|--------|------|
| objectId | 业务侧(推荐业务后端发号) | 逻辑对象 ID,上传前必须已有;全局唯一由业务保证 |
| businessCode | 业务侧 / SDK 默认配置 | 当前上传接口可选但部署上通常固定,如测试环境 dianshu-test |
| parameter.cosKey / COS Key | getUploadEndpoint 返回 | 物理存储路径,SDK 默认直接使用 |
| parameter.dynamicData.url | getUploadEndpoint 返回 | 动态 STS:getCosTmpCredential |
| UploadId | 腾讯云 COS multipartInit | 分片会话 ID;业务 / SDK 不构造,由 cos-nodejs-sdk-v5 缓存与复用 |
| dos://... | finishUploadObject 返回 | 上传完成后的逻辑地址 |
当前典枢客户端旧链路(cos/tmp/credential + cos/upload/completed)没有 objectId 语义;接入本 SDK 时需要业务后端增加「上传前发号」或由调用方自行生成并与业务对账。
Upload sequence
对齐设计文档 / 流程图:
POST /dos/api/getUploadEndpoint— 返回protocol=cos+version+objectUrl+parameter.{bucket,region,cosKey,dynamicData.url}POST /dos/api/startUploadObject— 后端创建dos_object,状态UPLOADING- SDK 直传 COS;签名时
POST dynamicData.url(getCosTmpCredential,{ cosKey })取 STS - SDK 计算 hash(默认 MD5,可插拔)
POST /dos/api/finishUploadObject— 传contentLength/contentHash,后端 placementREADY,返回dos://...
Pause / resume 策略(COS)
腾讯云 Node SDK 行为要点(本仓库 MockCos 按同样规则模拟):
| API | 效果 |
|-----|------|
| pauseTask | 停止传输;不清除 UploadId 的内存 using 标记 |
| restartTask | 仅当任务仍在当前进程队列且状态为 paused / error 时有效 |
| cancelTask | 清除 using;磁盘上的 UploadId 缓存仍保留 |
| 再次 uploadFile | 若 UploadId 仍被 using → 跳过并 重新开 multipart(从头传);若已释放 → 按缓存 UploadId 断点续传 |
本 SDK 对上层统一 pause / resume,与 dianshu-website 上传暂停语义对齐:
- 暂停:
pause→cancelTask(释放 UploadIdusing)+ 落盘 checkpoint=paused - 恢复:
resume→ 刷新 STS 后再次uploadFile,依赖 COS 本地 UploadId 缓存续传分片 - 进程退出 / 崩溃:同 2;
restartTask仅在队列里仍有 paused/error 任务时作为兜底 - Checkpoint 使用 SQLite:通用列(objectId / status / filePath / protocol…)+ 协议私有
protocol_state_json(COS:bucket / region / cosKey / taskId)。STS 与事件不落盘。
暂停需要
FilePath上传。COS 对 stream Body 不支持pauseTask。
UploadId 本地缓存键(SDK 内部,与腾讯实现一致)依赖:同一 FilePath、size / mtime / ctime、ChunkSize、Bucket、Key。续传时这些必须保持不变。
Auth
默认 TokenAuthProvider,请求头对齐当前 DOS 接口:
Authorization: Bearer <jwt>tokenUser-Agent:DataPubToolClient/<version>Client-VersionReferer:app://.
可注入自定义 AuthProvider。CasdoorNodeAuthProvider 为业务后端节点体系预留(暂未实现,调用会抛 NOT_IMPLEMENTED)。
import { DosClient, TokenAuthProvider } from "@yeez-tech/dos-sdk";
new DosClient({
baseUrl,
auth: new TokenAuthProvider({ token, clientVersion: "1.0.0" }),
});Hash
默认 Md5HashProvider。实现 HashProvider(algorithm + hashFile)即可替换。
Options 摘要
| 字段 | 说明 |
|------|------|
| baseUrl | DOSBackendService 根地址 |
| token / auth | 鉴权;token 为 TokenAuthProvider 快捷方式 |
| businessCode | client 级默认业务编码,可在 putObject 时覆盖 |
| userAgent / clientVersion | UA 与 Client-Version |
| hashProvider | 可插拔哈希 |
| checkpointStore / checkpointDbPath | checkpoint 存储;默认 SQLite |
| cosSliceSize | 触发分片上传的阈值(默认 5MB) |
| cosUploader | 可选注入,生产无需传;测例用来挂 Mock COS |
Testing
npm i
npm test
npm run build
npm run typecheck测例结构:
test/
sdk.test.ts # 基础:hash / sqlite / auth / 简单 putObject
cos.mock.test.ts # MockCos:UploadId / using / pause / cancel / 跨进程
client.upload.test.ts # 普通上传、软暂停、崩溃 reopen、并发、cancel
client.interrupt.worker.test.ts # SIGKILL(断电)/ SIGTERM(优雅)后 resume
workers/interrupt-upload-worker.ts
support/
mockCos.ts # 对齐腾讯语义的 COS mock(状态可落盘)
mockDosServer.ts # 本地 mock DOS HTTP
interruptWorker.ts # node --import tsx 拉起 worker
harness.tsWorker 中断约定(对齐 dianshu-file-transfer)
跨进程中断必须用:
node --import tsx <worker.ts> <config.json>不要用 tsx/cli 起 worker:tsx 会再 fork 一层,对 wrapper 发 SIGKILL 后真实上传进程可能仍存活,导致和父进程 resume 抢同一份状态。test/support/interruptWorker.ts 的 spawnTsWorker 已按上述方式实现。
- 断电 / 不优雅:worker 上传到中段 → 父进程
SIGKILL→ 同checkpointDbPath+ mock COS 状态文件 reopen →resume - 优雅退出:worker 收
SIGTERM→pause→ 打出graceful-exit→ 父进程 reopen →resume
Project layout
src/
client/DosClient.ts # putObject / pause / resume / cancel
api/DosBackendApi.ts # /dos/api/*
auth/ # AuthProvider + Token / Casdoor stub
hash/ # HashProvider + Md5
protocol/cos/ # CosUploader
store/ # CheckpointStore + Sqlite
test/ # 见上方 TestingScripts
npm i
npm test
npm run build
npm run typecheckLicense
MIT
