溯仑智算
溯仑智算 SU.RUN开发者文档
获取接入支持
溯仑智算
溯仑智算 SU.RUN
开发者文档
开始使用
文档中心
阅读路径、接入顺序与专题总览
存储接入快速开始
第一次接入时优先阅读
存储接入总览
能力边界、使用路径与整体说明
本地配置与验证指南
在本地完成示例接口与最小验证
上传与下载
授权上传指南
浏览器、App 与客户端上传方式
授权下载指南
受控下载、权限判断与短时授权
前端直传安全规范
长期密钥保护与权限边界
大文件上传实战
大文件上传、重试与断点续传
规划与治理
文件路径命名规范
存储桶、路径、目录与归档规则
自定义域名接入
品牌化访问、证书与上线准备
接口说明
服务端接入方式、鉴权边界与返回结构
最小后端接入示例
上传下载授权与确认入库的完整串联
代码示例
JavaScript、Python 与 Go 接入示例
AI 与工作流
AI 环境接入说明
常见 AI 环境的接入说明
AI 训练数据与结果回写规范
模型、数据集、日志与输出治理
排障与帮助
错误码与问题排查
常见异常定位与恢复建议
接入常见问题总览
按角色整理的常见问题说明
支持中心
获取接入支持
获取接入支持
开始使用
文档中心存储接入快速开始存储接入总览本地配置与验证指南
上传与下载
授权上传指南授权下载指南前端直传安全规范大文件上传实战
规划与治理
文件路径命名规范自定义域名接入接口说明最小后端接入示例代码示例
AI 与工作流
AI 环境接入说明AI 训练数据与结果回写规范
排障与帮助
错误码与问题排查接入常见问题总览
当前文档
上传下载授权与确认入库的完整串联
文档中心

最小后端接入示例

本文档面向需要先完成一版最小可用上传下载闭环的团队,按上传授权、前端直传、确认入库和下载授权的顺序给出示例。

后端接入
support@su.run

最小可用后端 / 上传下载闭环 / 业务确认入库 / 权限收口

上传授权

由后端统一决定 objectKey 和有效期

前端先请求业务侧 `/api/storage/presign-upload`,由后端决定 objectKey、有效期、权限边界和 requestId。

文件写入

真正的大文件不应该穿过业务服务器中转

前端拿到 uploadUrl 后直接对存储服务发 PUT,请求体只传文件本体,不再经过业务服务器。

确认入库

文件存在不代表业务已经完成

上传完成后,前端把 objectKey、文件名、大小和业务主键发回业务后端,由后端确认绑定关系并写入数据库。

下载授权

下载时再次收口权限边界

后续查看、下载或回放文件时,前端不直接拼接访问地址,而是再次走 `/api/storage/presign-download` 获取短时授权。

建议接入顺序

先签名,再上传,再入库,最后下载

  • 前端先请求上传授权接口,后端统一生成 objectKey 与 requestId。
  • 前端对 uploadUrl 发 PUT,把文件本体直接写入存储服务。
  • 上传成功后,前端把 objectKey 与业务上下文发回确认入库接口。
  • 后端写入业务主表或关联表,并把 objectKey 绑定到业务记录。
  • 后续页面读取文件时,只读取数据库里的业务记录,不依赖前端缓存 objectKey。

最小上传授权 Route

仓库里的示例 route 可以作为第一版参考

这一层的主要职责是识别当前用户、校验请求体、生成 objectKey 和 uploadUrl,并把统一结果结构返回给前端。

export async function POST(request: Request) {
  const user = await requireCurrentUser(request);
  const body = await request.json();

  const result = await createStoragePresignUploadDemo({
    userId: user.id,
    fileName: body.fileName,
    contentType: body.contentType,
    resourceType: body.resourceType ?? 'project-cover',
  });

  return Response.json(result);
}

前端上传调用

上传通常包含两次请求

第一次请求自己的上传授权接口,第二次再把文件 PUT 到 uploadUrl。正式项目里,上传成功后通常还需要继续确认入库,而不是只把 objectKey 暂存在前端内存里。

async function uploadProjectFile(file: File, accessToken: string) {
  const presignResponse = await fetch('/api/storage/presign-upload', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${accessToken}`,
    },
    body: JSON.stringify({
      fileName: file.name,
      contentType: file.type,
      resourceType: 'project-cover',
    }),
  });

  const presignResult = await presignResponse.json();

  await fetch(presignResult.uploadUrl, {
    method: 'PUT',
    headers: {
      'Content-Type': file.type,
    },
    body: file,
  });

  await fetch('/api/storage/confirm-upload', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${accessToken}`,
    },
    body: JSON.stringify({
      objectKey: presignResult.objectKey,
      fileName: file.name,
      contentType: file.type,
      size: file.size,
      resourceType: 'project-cover',
      projectId: 'proj_xxx',
    }),
  });

  return presignResult.objectKey;
}

确认入库接口

这是让上传流程进入正式业务记录的关键一步

当前仓库里已经提供 `/api/storage/confirm-upload` 这条示例 route。它会先校验当前用户目录,再回查对象是否已经上传成功,最后返回统一的 asset 结果结构。默认示例不会直接写数据库,但已经把确认入库前需要完成的校验落到了代码里。

export async function POST(request: Request) {
  const user = await requireCurrentUser(request);
  const body = await request.json();

  const result = await confirmStoragePresignUploadDemo({
    userId: user.id,
    objectKey: body.objectKey,
    fileName: body.fileName,
    contentType: body.contentType,
    size: body.size,
    resourceType: body.resourceType,
    projectId: body.projectId,
  });

  return Response.json(result);
}

建议的业务记录结构

至少把 objectKey、状态和业务主键落下来

{
  "id": "asset_xxx",
  "projectId": "proj_xxx",
  "objectKey": "docs-demo/<user-id>/project-cover/2026/06/abc123-demo-cover.png",
  "fileName": "demo-cover.png",
  "contentType": "image/png",
  "size": 248731,
  "status": "completed",
  "createdAt": "2026-06-10T08:00:00.000Z"
}

下载授权 Route

最稳的方式是先查业务记录,再决定是否签发

export async function POST(request: Request) {
  const user = await requireCurrentUser(request);
  const body = await request.json();

  const fileRecord = await findProjectAsset(body.assetId, user.id);

  const result = await createStoragePresignDownloadDemo({
    userId: user.id,
    objectKey: fileRecord.objectKey,
  });

  return Response.json(result);
}

前端下载调用

先看错误写法,再看正确写法

不推荐
async function createDownloadLink(assetId: string, accessToken: string) {
  const response = await fetch('/api/storage/presign-download', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${accessToken}`,
    },
    body: JSON.stringify({
      objectKey: assetId,
    }),
  });

  return response.json();
}
更稳妥的第一版
async function createDownloadLink(objectKey: string, accessToken: string) {
  const response = await fetch('/api/storage/presign-download', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${accessToken}`,
    },
    body: JSON.stringify({
      objectKey,
    }),
  });

  return response.json();
}

上线前自检

以下五条决定第一版是否具备可交付性

  • 上传授权成功,但还没有确认入库时,业务页面不应把对象当成正式可用资源。
  • 确认入库接口必须校验 objectKey 是否属于当前用户、组织或项目可写目录。
  • 下载授权不要直接吃前端传来的任意 objectKey,优先从业务主表反查真实对象路径。
  • 所有关键链路都要带 requestId,上传授权、确认入库、下载授权至少三处要能串起来。
  • 如果后续接 AI 工作流,建议把 resourceType、projectId、taskId 等业务上下文一起落库。

接入常见问题

为什么上传成功后还要再做一次确认入库?

因为对象已经写进存储,只代表“文件存在”,不代表“业务上可见”。确认入库负责把对象路径和业务主键、用户权限、状态流转真正绑定起来。

下载授权为什么更推荐从业务记录反查 objectKey?

因为这样可以避免前端随手传任意 objectKey。真正安全的做法是前端传业务记录 ID,由后端查出对应 objectKey 后再决定是否签发下载。

本页里的 confirm-upload route 是不是仓库里已经存在?

是的,仓库里现在已经有 `/api/storage/confirm-upload` 这条 demo route。它会校验目录范围并确认对象存在,再返回统一的 asset 结构;正式产品版可在此基础上继续接入数据库写入。

上一篇
接口说明
服务端接入方式、鉴权边界与返回结构
下一篇
代码示例
JavaScript、Python 与 Go 接入示例
更多帮助

你可能还需要这些帮助入口

文档中心帮助你完成接入,客户服务页帮助你了解采购、支持与服务信息。

邮件支持

需要迁移协助或接入支持时,可以直接进入邮件支持入口。

进入支持中心

客户常见问题

采购、测试申请、迁移安排与售后问题,可先查看这份常见问题说明。

查看常见问题

价格与采购说明

需要确认容量区间、采购方式、续费与对账说明时,可从这里继续查看。

查看价格说明