资产存储
资产存储默认开通:普通应用与静态站点(顶级)创建即拥有独立的对象存储空间,无需申请;用不用、传不传由你决定。默认配额 15 GiB,需要更大空间或无限配额时联系管理员调整。文件分两类前缀:
- 公开:有固定公网地址,经 CDN 分发——图片、样式、下载包等公开静态资源放这里。
- 私有:无公开地址,应用在集群内向存储服务获取临时链接——付费内容、内部文件放这里。
容器只出动态内容、重资产走对象存储直连,动静分离。配额按应用独立(公开+私有合计)。
静态站点应用仅支持公开资产:私有资产需要应用运行时持有访问凭证换取临时链接,静态站点没有运行时进程。站点体积配额之外的大文件(视频、下载包、图集等)放公开资产,按公开地址直接在页面里引用即可——上传管理同样在「存储」Tab,无需任何凭证与环境变量。
文件管理(平台控制台)
浏览、删除、缓存刷新在网页端完成:应用详情 →「存储」Tab——选择公开/私有前缀、逐层浏览、多选上传(单文件 ≤5GB)、删除、刷新 CDN 缓存(目录级每天 5 次)。管理员在「资产存储」页调整各应用配额。
上传有三个入口:网页端「存储」Tab、CLI nae assets 命令(见下)、或应用运行时直接调存储服务上传(普通应用,见下文「存储服务接口」;静态站点应用仅网页端与 CLI)。
- 可见性上传时定死,不可改;要换可见性 = 删除后重传。
- 公开前缀不收 HTML 类文档(网页发布请使用静态站点功能)。
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 缓存分发。覆盖更新同名文件后,边缘缓存不会立即失效——两种做法:
- 推荐:文件名带版本号或内容哈希(
app.3f2a.css),新版本新文件名,永不撞缓存; - 覆盖后在「存储」Tab 里刷新缓存(目录级每天限 5 次,能用哈希文件名就别依赖刷新)。
私有资产
私有文件没有公开地址,全生命周期就三件事,网页端与应用侧都能做:
| 动作 | 网页端(「存储」Tab) | 应用侧(存储服务接口) |
|---|---|---|
| 上传 | 前缀切到私有后上传 | 流式 PUT /v1/object/<路径>?visibility=private,或签名上传请求体里 "visibility": "private" |
| 访问 | 逐层浏览,点文件取临时链接 | POST /v1/presign-get 用路径换临时链接 |
| 删除 | 勾选文件删除 | POST /v1/delete 请求体里 "visibility": "private" + 路径列表 |
上传/删除与公开资产共用同一套接口,区别只有 visibility 一个参数;访问是唯一的分叉——公开文件拼固定 URL,私有文件拿相对路径现换临时链接。各接口细节见下文「存储服务接口」。
上传后把相对路径记在应用自己的数据里(数据库、桶内清单文件均可):应用侧不提供列举,路径就是访问与删除的钥匙。
存储服务接口(应用侧)
存储服务对应用开放三类能力(均只操作应用自己知道的路径,不提供文件列举):
- 换临时下载链接:
POST /v1/presign-get; - 上传:签名上传
POST /v1/presign-put(换票后字节直连对象存储),或流式上传PUT /v1/object/<路径>(一步上传,字节经存储服务转交); - 删除:
POST /v1/delete(显式路径列表——应用清理自己产生的临时文件用;整体清空只在控制台)。
文件的浏览与缓存刷新在平台控制台完成,对应端点一律 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):
internal: false(默认)——公网临时链接,下发给浏览器用;internal: true——集群内网直连地址,应用自己取文件用(不出公网,大文件也快)。
临时链接的有效期就是它的访问控制——别把它当永久地址存库,存相对路径、用时现取。
同一个文件在同一时间窗口内获取的链接是同一张(有效期至少 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: <字节数>
<文件字节>
visibility必填:public或private,上传时定死;Content-Type必填:对象的 MIME,下载方原样收到;- 必须带
Content-Length(不支持 chunked 传输编码)——长度未知的流请先落盘/缓冲,或改用 presign-put; - 同名路径重复上传即覆盖(配额只计差额)。
返回 { "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 }
size为申报的文件字节数(配额预检用);internal: true——返回集群内网直连地址,应用在集群内上传用这个(不出公网,大文件也快);internal: false(默认)返回公网地址;- 返回
{ "url", "key", "public_url", "expires_at", "headers" }:在expires_at(签发后 5 分钟)前,向url发 PUT,请求头须带headers中逐字相等的各头(Content-Type参与签名,改了必 403)。
PUT <url>
Content-Type: <headers 中的值,逐字相等>
Cache-Control: <headers 中的值>
<文件字节>
两种模式怎么选:日常上传用流式(一次请求、无票据管理);GB 级大文件或想让字节绕开存储服务直连对象存储时用签名上传。
删除:POST /v1/delete
{ "visibility": "private", "paths": ["reports/2026-08.pdf", "tmp/cache.bin"] }
删除自己上传的对象(如临时文件的定期清理)。返回 { "deleted": <数量> }。要点:
paths为显式相对路径列表(单次 ≤500),与上传时一致——应用自己记录路径,应用侧不提供列举;- 删除幂等:路径不存在不报错;
- 整体清空(all)应用侧不开放,请在平台控制台「存储」页操作。
应用产生的临时对象(会话附件、导出缓存等)记得配套清理逻辑——存储配额是按应用算的,只写不删迟早撞配额。做法:上传时把路径与时间记进应用自己的台账(数据库/桶内清单文件均可),定时把过期路径批量传给本端点。
完整示例(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 次(控制台操作) |