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

授权上传指南

授权上传的重点是在不暴露长期密钥的前提下,让浏览器、App 或第三方客户端安全完成上传。

上传接入
support@su.run

浏览器上传 / App 上传 / 受控直传方案

建议流程

STEP 1
前端上传前,先向业务服务端申请 uploadUrl。
STEP 2
服务端根据业务身份、存储桶、文件路径和文件类型生成短时上传授权。
STEP 3
前端使用 PUT 或表单上传把文件传到存储服务。
STEP 4
上传成功后,将 objectKey 回传业务服务端完成入库确认。

前端负责什么

前端只负责采集文件、调用业务上传授权接口、使用 uploadUrl 提交文件,并在成功后把 objectKey 交回业务后端。前端不负责生成关键路径,也不负责保存长期密钥。

后端负责什么

后端负责鉴权、生成文件路径、限制文件类型和目录范围、签发 uploadUrl、记录 requestId,并在上传完成后把文件与业务记录关联起来。

推荐请求结构

POST /api/storage/presign-upload
{
  "fileName": "avatar.png",
  "contentType": "image/png",
  "resourceType": "user-avatar"
}

推荐返回结构

{
  "success": true,
  "bucket": "media-assets",
  "objectKey": "uploads/2026/06/avatar.png",
  "uploadUrl": "https://your-upload-url",
  "expiresIn": 600,
  "requestId": "req_xxx"
}

使用建议

  • 文件路径建议由后端统一生成,不让前端自行拼接关键目录。
  • uploadUrl 必须带有效期,避免形成可长期传播的上传入口。
  • 业务系统建议在上传后再做一次确认入库,不把上传动作直接当成业务完成。
  • 前后端日志建议都带 requestId,方便排障。

第一步:前端申请上传授权

前端不要直接上传到存储服务,也不要自行拼接 objectKey。第一步应先请求业务后端,由后端完成鉴权和路径生成。

POST /api/storage/presign-upload
{
  "fileName": "avatar.png",
  "contentType": "image/png",
  "resourceType": "user-avatar"
}

第二步:后端生成 objectKey 和 uploadUrl

后端根据用户身份、项目、业务类型和文件类型生成安全的文件路径,并返回 bucket、objectKey、uploadUrl、expiresIn 和 requestId。

{
  "success": true,
  "bucket": "media-assets",
  "objectKey": "uploads/2026/06/avatar.png",
  "uploadUrl": "https://your-upload-url",
  "expiresIn": 600,
  "requestId": "req_xxx"
}

第三步:浏览器上传并确认入库

浏览器只负责拿 uploadUrl 执行 PUT 上传。上传成功后,再调用业务确认接口,把 objectKey 挂到用户、项目或素材记录上。

async function uploadFile(file: File) {
  const authResponse = await fetch('/api/storage/presign-upload', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      fileName: file.name,
      contentType: file.type,
    }),
  });

  const { uploadUrl, objectKey } = await authResponse.json();

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

  await fetch('/api/assets/confirm-upload', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ objectKey }),
  });
}

后端签发示例

推荐由业务后端统一完成鉴权、路径生成和 uploadUrl 签发。下面是一段最小示例。

import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';

const client = new S3Client({
  region: 'auto',
  endpoint: process.env.SURUN_S3_ENDPOINT,
  credentials: {
    accessKeyId: process.env.SURUN_ACCESS_KEY_ID!,
    secretAccessKey: process.env.SURUN_SECRET_ACCESS_KEY!,
  },
});

export async function createUploadAuthorization(params: {
  fileName: string;
  contentType: string;
  userId: string;
}) {
  const objectKey = 'uploads/' + params.userId + '/' + String(Date.now()) + '-' + params.fileName;

  const command = new PutObjectCommand({
    Bucket: 'media-assets',
    Key: objectKey,
    ContentType: params.contentType,
  });

  const uploadUrl = await getSignedUrl(client, command, { expiresIn: 600 });

  return {
    bucket: 'media-assets',
    objectKey,
    uploadUrl,
    expiresIn: 600,
  };
}

前端上传示例

前端只负责申请授权、提交文件和回传 objectKey,不应直接保存或计算长期凭证。

async function uploadFile(file: File) {
  const authResponse = await fetch('/api/storage/presign-upload', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      fileName: file.name,
      contentType: file.type,
    }),
  });

  const { uploadUrl, objectKey } = await authResponse.json();

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

  await fetch('/api/assets/confirm-upload', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ objectKey }),
  });
}

上传授权刚拿到就失效

优先检查后端签发时设置的 expiresIn 是否过短,以及前端是否在拿到授权后等待太久才开始上传。

浏览器上传返回 403

通常要检查对象路径是否超出后端允许范围、Content-Type 是否不匹配,或者签发时的 bucket 与实际上传目标不一致。

文件传上去了,但业务里找不到

这通常不是存储问题,而是上传完成后没有把 objectKey 回传业务后端确认入库。上传成功与业务完成是两件事。

排查时优先检查什么

  • 先确认请求签名接口的是业务后端,而不是浏览器直接调用存储服务。
  • 检查后端生成的 objectKey 是否符合业务目录规则。
  • 确认 Content-Type 与实际上传文件类型一致。
  • 确认 uploadUrl 没有被缓存、转发或在过期后重复使用。
  • 确认上传完成后真的执行了 confirm-upload,而不是只看浏览器请求成功。

不同业务场景建议

如果要做头像上传

建议固定资源类型和目录规则,例如 avatars/{userId}/。这样后续替换头像、清理旧资源和做访问控制都更简单。

如果要做业务附件上传

建议 objectKey 里带上项目、工单或业务主键,而不是只保留文件名。这样后续确认入库和排查问题时不会丢失上下文。

如果要做 App 或第三方客户端上传

流程保持一致,均应先通过业务后端获取 uploadUrl。变化的是调用端,而不是权限模型。

SECURITY NOTICE

上传授权安全边界

  • 长期密钥只保存在业务服务端。
  • 文件路径和 Content-Type 由后端校验。
  • 上传授权必须设置过期时间。
  • 上传成功后仍建议由业务后端做确认。

上线前检查清单

  • 上传接口只运行在服务端,不在浏览器里保存长期密钥。
  • 对象路径由后端生成,前端不能随意覆盖关键目录。
  • 上传授权有明确有效期,并限制 bucket、Content-Type 和对象路径。
  • 上传完成后存在确认入库动作,而不是只看浏览器上传成功。
  • 日志里能通过 requestId 关联前端报错和服务端签发记录。

生产上线补充检查

  • 上传授权接口有登录态或业务身份校验。
  • 对象路径由后端统一生成,且按业务环境和资源类型分层。
  • 上传授权有效期足够短,且和业务场景匹配。
  • 前端报错、后端签发、对象写入三段日志能通过 requestId 对齐。
  • 用户可见报错是业务可理解文案,不直接暴露底层异常细节。

接入常见问题

为什么前端不能自己决定 objectKey?

因为 objectKey 本质上就是业务路径。把它完全交给前端,意味着客户端可能写入不应开放的目录,也会让后续审计和治理失控。

上传成功后为什么还要调一次 confirm 接口?

因为对象传到存储里,只代表文件存在了,不代表业务系统已经认领它。很多场景都需要把 objectKey 与用户、项目、工单或素材记录绑定起来。

授权上传是否适合大文件场景?

可以,但如果文件很大、网络不稳定或需要断点续传,通常更推荐走分段上传方案。

上一篇
本地配置与验证指南
在本地完成示例接口与最小验证
下一篇
授权下载指南
受控下载、权限判断与短时授权
更多帮助

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

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

邮件支持

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

进入支持中心

客户常见问题

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

查看常见问题

价格与采购说明

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

查看价格说明