资产存储

资产存储默认开通:普通应用与静态站点(顶级)创建即拥有独立的对象存储空间,无需申请;用不用、传不传由你决定。默认配额 15 GiB,需要更大空间或无限配额时联系管理员调整。文件分两类前缀:

容器只出动态内容、重资产走对象存储直连,动静分离。配额按应用独立(公开+私有合计)。

静态站点应用仅支持公开资产:私有资产需要应用运行时持有访问凭证换取临时链接,静态站点没有运行时进程。站点体积配额之外的大文件(视频、下载包、图集等)放公开资产,按公开地址直接在页面里引用即可——上传管理同样在「存储」Tab,无需任何凭证与环境变量。

文件管理(平台控制台)

浏览、删除、缓存刷新在网页端完成:应用详情 →「存储」Tab——选择公开/私有前缀、逐层浏览、多选上传(单文件 ≤5GB)、删除、刷新 CDN 缓存(目录级每天 5 次)。管理员在「资产存储」页调整各应用配额。

上传有三个入口:网页端「存储」Tab、CLI nae assets 命令(见下)、或应用运行时直接调存储服务上传(普通应用,见下文「存储服务接口」;静态站点应用仅网页端与 CLI)。

CLI 命令

网页端「存储」Tab 的能力在命令行的对应物(开发者本机操作自己应用的资产,走个人访问令牌鉴权,与应用运行时凭据无关):

命令 说明
nae assets quota <appid> 配额与用量(公开/私有分列)
nae assets ls <appid> [dir] [-V public\|private] 逐层浏览(--marker 翻页续读)
nae assets upload <appid> <file> [--dest 路径] [-V ...] 上传:平台签票 → 本机直传对象存储,字节不经平台
nae assets link <appid> <path> 给私有文件签限时访问链接(公开文件直接用 ls 返回的 url
nae assets rm <appid> <path...> 删除指定文件;--all --yes 清空整段(危险)
nae assets refresh <appid> [path...] 刷新公开资产 CDN 缓存(给路径逐文件,否则整目录;计每日刷新配额)
nae assets stats <appid> 近 7 天访问简报(边缘出口口径,延迟数小时)
nae assets upload myapp ./logo.png                    # 公开:上传即有 CDN 公网地址
nae assets upload myapp ./v.mp4 --dest media/v.mp4    # 带目录
nae assets upload myapp ./secret.json -V private      # 私有:访问走 nae assets link
nae assets ls myapp media/                            # 浏览 media/ 层

公开上传即刻获得公网地址,敏感文件务必 -V private。上传的 Content-Type 按扩展名推断,特殊类型用 --content-type 指定。

凭据

普通应用启动/重启一次后,平台自动注入三个环境变量:

变量 含义
NAE_ASSETS_BASE 公开资产地址前缀(https://…/files/<appid>),公开文件 URL = 它 + /相对路径
NAE_STORAGE_API 存储服务地址(集群内 DNS,仅集群内可达)
NAE_STORAGE_KEY 换取临时链接的凭据(naes_ 前缀)

令牌只存在于容器环境变量里——平台网页上没有、也不会展示明文(服务端只存哈希),不需要到任何页面「领取」。应用内直接读 process.env.NAE_STORAGE_KEY(或所用语言的等价物)即可。

令牌是应用级凭据:不要打进日志、不要下发给浏览器。前端需要访问私有文件时,由你的后端换好临时链接再下发(见下文 presign-get)。

判断凭据是否就位:这三个变量存在即可用。变量缺失说明应用还没在本次功能上线后重启过——重启一次即注入。

公开资产

公开文件 URL 固定,可直接外发:

${NAE_ASSETS_BASE}/logo.png
${NAE_ASSETS_BASE}/downloads/app-v2.zip

公开地址经 CDN 缓存分发。覆盖更新同名文件后,边缘缓存不会立即失效——两种做法:

私有资产

私有文件没有公开地址,全生命周期就三件事,网页端与应用侧都能做:

动作 网页端(「存储」Tab) 应用侧(存储服务接口)
上传 前缀切到私有后上传 流式 PUT /v1/object/<路径>?visibility=private,或签名上传请求体里 "visibility": "private"
访问 逐层浏览,点文件取临时链接 POST /v1/presign-get 用路径换临时链接
删除 勾选文件删除 POST /v1/delete 请求体里 "visibility": "private" + 路径列表

上传/删除与公开资产共用同一套接口,区别只有 visibility 一个参数;访问是唯一的分叉——公开文件拼固定 URL,私有文件拿相对路径现换临时链接。各接口细节见下文「存储服务接口」。

上传后把相对路径记在应用自己的数据里(数据库、桶内清单文件均可):应用侧不提供列举,路径就是访问与删除的钥匙

存储服务接口(应用侧)

存储服务对应用开放三类能力(均只操作应用自己知道的路径,不提供文件列举):

文件的浏览与缓存刷新在平台控制台完成,对应端点一律 403。静态站点应用不支持应用侧上传/删除(没有运行时进程,也不注入凭据),文件管理只在网页端。

请求带鉴权头:

Authorization: Bearer ${NAE_STORAGE_KEY}

成功返回数据对象本身;失败返回 {"error": "原因"}(HTTP 状态码对应错误类别)。

获取链接:POST /v1/presign-get

{ "path": "reports/2026-08.pdf", "internal": false }

path 是上传时的相对路径——由应用自己的数据记录,应用侧不提供文件列举。返回 { "url", "expires_at" }——链接是临时的expires_at 后失效,过期重新获取。私有文件专用(公开文件直接拼 NAE_ASSETS_BASE):

临时链接的有效期就是它的访问控制——别把它当永久地址存库,存相对路径、用时现取。

同一个文件在同一时间窗口内获取的链接是同一张(有效期至少 1 小时,以 expires_at 为准)。这意味着:可以放心把 url 缓存到 expires_at 再重新获取,不必每次请求都换链接;重复访问同一链接会命中 CDN 缓存,不产生重复回源。

流式上传:PUT /v1/object/<路径>

最省事的上传方式:一次请求,请求体就是文件字节。

PUT ${NAE_STORAGE_API}/v1/object/reports/2026-08.pdf?visibility=private
Authorization: Bearer ${NAE_STORAGE_KEY}
Content-Type: application/pdf
Content-Length: <字节数>

<文件字节>

返回 { "key", "size", "public_url" }——公开对象带最终公网地址,私有对象 public_url 为空(访问经 presign-get)。

签名上传:POST /v1/presign-put

大文件/高并发场景的上传方式:先换一张预签名上传票,然后把字节直接 PUT 到对象存储,不经过存储服务转交。

{ "path": "reports/2026-08.pdf", "visibility": "private", "content_type": "application/pdf", "size": 1048576, "internal": true }
PUT <url>
Content-Type: <headers 中的值,逐字相等>
Cache-Control: <headers 中的值>

<文件字节>

两种模式怎么选:日常上传用流式(一次请求、无票据管理);GB 级大文件或想让字节绕开存储服务直连对象存储时用签名上传

删除:POST /v1/delete

{ "visibility": "private", "paths": ["reports/2026-08.pdf", "tmp/cache.bin"] }

删除自己上传的对象(如临时文件的定期清理)。返回 { "deleted": <数量> }。要点:

应用产生的临时对象(会话附件、导出缓存等)记得配套清理逻辑——存储配额是按应用算的,只写不删迟早撞配额。做法:上传时把路径与时间记进应用自己的台账(数据库/桶内清单文件均可),定时把过期路径批量传给本端点。

完整示例(Node.js)

const api = process.env.NAE_STORAGE_API
const H = {
  Authorization: `Bearer ${process.env.NAE_STORAGE_KEY}`,
  'Content-Type': 'application/json',
}

// 流式上传(一步到位;fs.readFileSync 换成任何 Buffer/Uint8Array 均可)
const data = fs.readFileSync('report.pdf')
const up = await (await fetch(`${api}/v1/object/reports/2026-08.pdf?visibility=private`, {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${process.env.NAE_STORAGE_KEY}`,
    'Content-Type': 'application/pdf',
    'Content-Length': String(data.length),
  },
  body: data,
})).json()
// up.key = 'data/<appid>/reports/2026-08.pdf'

// 签名上传(大文件:换票后直连对象存储,internal=true 走集群内网)
const t = await (await fetch(`${api}/v1/presign-put`, {
  method: 'POST', headers: H,
  body: JSON.stringify({ path: 'videos/intro.mp4', visibility: 'public',
    content_type: 'video/mp4', size: data.length, internal: true }),
})).json()
await fetch(t.url, { method: 'PUT', headers: t.headers, body: data })
// 公开对象最终地址:t.public_url

// 获取临时链接(下发给浏览器)
const g = await (await fetch(`${api}/v1/presign-get`, {
  method: 'POST', headers: H,
  body: JSON.stringify({ path: 'reports/2026-08.pdf' }),
})).json()
// g.url 在 g.expires_at 前有效,下发给前端

// 应用自己读文件:internal=true 走集群内网
const i = await (await fetch(`${api}/v1/presign-get`, {
  method: 'POST', headers: H,
  body: JSON.stringify({ path: 'reports/2026-08.pdf', internal: true }),
})).json()
const bytes = await (await fetch(i.url)).arrayBuffer()

其它语言同理:纯 HTTP + JSON,无 SDK 依赖。

约束速览

规格
文件浏览/缓存刷新/整体清空 仅平台控制台「存储」Tab
应用侧 API presign-get 换链接 + presign-put/PUT /v1/object/<路径> 上传 + delete 删已知路径(其余端点 403)
应用侧写操作 仅普通应用;静态站点应用仅网页端管理
配额 按应用独立,公开+私有合计;用量见「存储」Tab
单文件 ≤ 5GB
流式上传 必须带 Content-Length(不支持 chunked)
可见性 上传时定死;改可见性 = 删除重传
公开前缀 不收 HTML 类文档
临时链接 限时(以返回的 expires_at 为准),过期重新获取
目录级缓存刷新 每天 5 次(控制台操作)