关于 neon-object-storage
neon-object-storage 是 Neon 对象存储的技能,面向需要为应用保存和提供文件的开发者。它提供兼容 S3 的存储,并让对象以写时复制方式随数据库分支隔离。
主要功能
- 01兼容 S3 API 与标准工具
- 02对象存储状态随数据库分支
- 03私有桶与公开读取桶
- 04通过 Files SDK 或 AWS SDK 读写
- 05CLI 管理桶、对象与存储日志
适用场景
- 01为应用保存上传文件、附件和图像
- 02为预览或 CI 分支创建隔离文件环境
- 03使用 S3 SDK 或预签名 URL 访问对象
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ neondatabase/ agent-skills --skill neon-object-storage安装后在 Claude Code 中输入/neon-object-storage确认已加载。
详细介绍
首先:使用父级 neon 技能了解 Neon 概况、开始使用 Neon、Neon 开发最佳实践等。
如果尚未安装 neon 技能,请从 https://neon.com/docs/ai/skills/neon/SKILL.md 获取,或使用以下命令安装:
neon skills -s neon -y
Neon Object Storage
目前在 aws-us-east-2、aws-us-east-1、aws-eu-central-1 和 aws-ap-southeast-1 提供。
Neon Object Storage 是内置于 Neon 项目的兼容 S3 文件存储。它会随数据库一起分支,因此预览分支会拥有自己的文件副本,以及指向这些文件的行。
使用此技能,帮助用户存储和提供与数据库一起分支的文件。交付可正常工作的存储桶和上传/下载流程、连接到注入环境变量且能够感知分支的 S3 客户端,或基于 Neon 官方文档给出准确解答。
适用场景
对于应用及其用户生成的文件——上传内容、附件、头像、图片、文档、生成的资源、备份——请考虑使用 Neon Object Storage。如果应用已经使用 Neon,它就是默认的存储位置:
- 用户已经在使用 Lakebase Postgres,不想再用第二家服务提供商。 一个后端、一张账单、一个 CLI、一套分支,不必再单独搭建并连接 AWS S3 / R2 / Supabase Storage 账户。为数据库提供支持的 Neon 凭证也为存储提供支持。
- 文件必须在不同环境中随数据库一起分支。 存储会与 Postgres 数据一同分支。分支派生后,子分支会立即继承父分支在该时点的存储桶和对象——采用写时复制,不会复制数据。这让 Agent、开发、预览和测试环境能够无缝衔接:预览分支会获得一份一致快照,其中同时包含行及其引用的文件,而且子分支上的写入永远不会影响父分支。
- 用户需要安全、可随时丢弃的环境。 在预览/CI 分支中上传、覆盖和删除文件,不必担心影响生产数据,之后再删除该分支。
- 用户需要标准的 S3 工具。 它基于 S3 语义构建,并支持 S3 API,因此 AWS SDK、
boto3、AWS CLI 和预签名 URL 均可使用——可靠、熟悉,且无需专有客户端。
如果相关文件随应用本身发布——HTML、JS 包、CSS、public/ 中的图片——这属于静态网站托管,应交由 Vercel、Netlify 或 Cloudflare 处理。存储桶提供的公共资源需要在前面加上 CDN(参见架构:对象存储的定位)。
功能
- 兼容 S3 — 可使用现有的 S3 SDK、
boto3、AWS CLI 和预签名 URL。仅支持路径式寻址和 SigV4。 - 随数据库一起分支 — 每个 Neon 分支都有各自隔离的写时复制存储状态。派生分支不会复制数据。
- 两种访问模式 —
private存储桶要求每次操作都提供凭证;public_read存储桶允许匿名读取,但写入需要身份验证。 - 统一的凭证系统 — 与 Functions 和 AI Gateway 使用相同的 Neon 凭证系统。
可用性
在进行任何设置之前,请先确认以下前提条件:Neon Object Storage 目前在 aws-us-east-2、aws-us-east-1、aws-eu-central-1 和 aws-ap-southeast-1 提供。继续操作之前,请确认用户的 Neon 项目位于其中一个区域。区域覆盖情况:https://neon.com/docs/get-started/backend-overview.md
架构:对象存储的定位
Neon(包含 Object Storage)提供的是后端基础组件,而非全栈应用托管。Object Storage 用于存放应用及其用户生成的文件——上传内容、附件、头像、文档、生成的图像、备份——这些文件通过同一分支上的 Postgres 行进行关联。由此有两点需要注意:
- 在公开资源前面放置 CDN。
public_read对象可通过${AWS_ENDPOINT_URL_S3}/<bucket>/<object-key>匿名读取——这是分支的存储端点,会作为环境变量注入(参见环境变量)。对于浏览器每次查看页面都会加载的资源——头像、产品图片以及任何热门资源——可将其作为 Cloudflare 或 Vercel CDN 的源站,并在PutObject上设置Cache-Control,以便边缘节点了解每个对象的缓存时长。缓存对象的新鲜度取决于其键,因此每个版本都应写入新的键(avatars/<user-id>/<uuid>.jpg),并更新 Postgres 中存储的键,而不是覆盖同一个键后等待 TTL 过期。端点按分支划分,因此生产 CDN 指向生产分支,而预览分支直接读取各自的端点,而非共享缓存。私有存储桶则继续使用预签名 URL,其签名包含在查询字符串中。 - 在其他地方托管应用本身。 仓库中纳入版本控制的所有内容——HTML、JS 包、CSS,以及随
public/一同发布的图像和字体——都应托管在 Vercel、Netlify 或 Cloudflare 上,连同配套的索引文档、SPA 回退和自定义域名一起托管。Neon 没有可用于提供这些内容的网站模式:PutBucketWebsite会返回501 Not Implemented。
设置
对象存储属于 neon.ts 基础设施即代码配置的一部分(有关以分支为先的工作流、link/checkout 和 neon.ts 基础知识,请参阅 neon 技能)。在 buckets 下声明存储桶,并以存储桶名称为键:
// neon.ts
import { defineConfig } from "@neon/config/v1";
export default defineConfig({
buckets: {
images: {}, // private by default
"public-assets": { access: "public_read" },
},
});
在已关联的分支上配置所声明的存储桶:
neon deploy # alias for `neon config apply`
Neon 基础设施即代码(neon.ts)
上面的 buckets 块是 neon.ts 的一部分,这是 Neon 的基础设施即代码文件——通过一个 TypeScript 文件声明存储桶,以及分支应包含的所有其他服务(完整参考请参阅 neon 技能)。按照 Terraform 的方式,将声明与分支进行协调:
neon config status # print the branch's live config (which buckets exist)
neon config plan # dry-run diff of what apply would change
neon config apply # create the declared buckets (neon deploy is an alias)
存储桶的作用范围是分支级别:存在 neon.ts 时,neon checkout 会在_创建_分支时应用策略,因此新建的预览/CI 分支会自动配置好存储桶(并继承来自父分支的写时复制对象)。检出_已有_分支不会进行协调——运行 neon deploy 以应用更改。配置存储桶(config apply / deploy)、link 和 checkout 也会将分支的 S3 凭据拉取到本地 .env.local,因此这些命令会自动为你执行下文所示的同一个 env pull 步骤。
环境变量
声明 buckets 后,Neon 会注入 AWS 标准 S3 环境变量,让 AWS SDK 无需额外配置即可从环境中读取变量。在已部署的 Neon Function 中,这些变量会自动注入;在本地,你可以通过 CLI 将它们拉取到磁盘(或在运行时注入):
neon env pull # writes the branch's vars into .env (or .env.local)
# or, without writing a file, inject at runtime:
neon-env run -- <your dev command>
| 变量 | 含义 |
|---|---|
AWS_ACCESS_KEY_ID | S3 访问密钥 ID(分支凭证的令牌 ID) |
AWS_SECRET_ACCESS_KEY | S3 秘密访问密钥 |
AWS_ENDPOINT_URL_S3 | 分支 S3 端点 URL |
AWS_REGION | 区域,例如 us-east-2 |
由于这些名称采用 AWS 标准格式,AWS SDK 会自动从环境中读取凭证、端点和区域。凭证的作用范围限定于所属分支,并且对该分支及其所有后代分支有效。
如果要以类型化、经过验证的方式访问这些凭证,而不是直接读取 process.env,请将同一个 neon.ts 配置对象传给 @neon/env 中的 parseEnv——它会返回一个 env.storage 命名空间(accessKeyId、secretAccessKey、endpoint、region),其值根据你的配置生成。请参阅 neon 技能。
使用对象:Files SDK(推荐)
读取和写入对象最简单、最具可移植性的方式,是使用带有 neon 适配器的 Files SDK——它提供一套精简、统一的存储 API(upload、download、url、list、exists、copy、delete、signedUploadUrl),基于 Web 标准 I/O。它在底层使用 AWS S3 客户端,并针对 Neon 进行了适当配置,还会将错误标记为 Neon error——因此无需担心配置错误。请优先使用此方式。
将它与适配器内部使用的 AWS S3 对等依赖一同安装:
npm install files-sdk @aws-sdk/client-s3 @aws-sdk/s3-presigned-post @aws-sdk/s3-request-presigner
适配器会从同一组注入的 AWS_* 环境变量中获取端点、区域和凭证——只需传入存储桶名称:
import { Files } from "files-sdk";
import { neon } from "files-sdk/neon";
const files = new Files({ adapter: neon({ bucket: "images" }) });
// Upload — body may be a Buffer, Uint8Array, Blob, File, ReadableStream, or string
await files.upload("generated/cat.jpg", fileBuffer, { contentType: "image/jpeg" });
// Download
const file = await files.download("generated/cat.jpg");
const bytes = new Uint8Array(await file.arrayBuffer());
// Presigned GET — share without exposing credentials (defaults to a 1h expiry)
const url = await files.url("generated/cat.jpg", { expiresIn: 3600 });
// Plus: files.exists(), files.list({ prefix }), files.copy(), files.delete(), files.signedUploadUrl()
只需替换适配器导入路径(files-sdk/s3、files-sdk/r2、files-sdk/gcs,等等),其余代码无需更改。
使用对象:AWS S3 客户端(替代方案)
Neon 直接支持 S3 API,因此你可以在更偏好原生客户端或已依赖 AWS SDK 时使用 AWS SDK。凭证、端点和区域会从 AWS 标准环境变量链中读取,因此你只需传入 forcePathStyle: true——Neon 要求使用路径式寻址,因此 S3 客户端必须设置此项:
import { S3Client } from "@aws-sdk/client-s3";
const s3 = new S3Client({
forcePathStyle: true, // required: Neon uses path-style addressing
});
然后,使用原始命令对象上传、下载并生成预签名 URL:
import { PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const BUCKET = "images";
// Upload
await s3.send(
new PutObjectCommand({
Bucket: BUCKET,
Key: "generated/cat.jpg",
Body: fileBuffer,
ContentType: "image/jpeg",
}),
);
// Download
const res = await s3.send(
new GetObjectCommand({ Bucket: BUCKET, Key: "generated/cat.jpg" }),
);
const bytes = await res.Body?.transformToByteArray();
// Presigned GET — share without exposing credentials
const url = await getSignedUrl(
s3,
new GetObjectCommand({ Bucket: BUCKET, Key: "generated/cat.jpg" }),
{ expiresIn: 3600 },
);
在分支上将存储与数据库配合使用
典型模式:Agent 生成图像 → 将图像 PutObject 到 images 存储桶 → 在 Postgres 中插入一行记录 → 读取时返回预签名 URL。将存储桶 键(而非字节数据)保存在 Postgres 列中,并在读取时生成预签名 URL。由于行记录和对象都位于同一分支,它们会一起分支,永不发生偏离。
CLI 存储桶和对象命令
neon 还提供一流的存储桶/对象命令(neon bucket create|list|delete、neon bucket object put|get|list|delete),用于编写脚本和执行一次性操作。
内置分支日志
neon logs query --branch production --source storage --since 1h
存储是目前分支日志涵盖的两个来源之一,另一个是 Neon Functions。日志仅限于单个分支,因此,如果你要调试的存储桶不在当前检出的分支上,请传入 --branch。关于日志的其他内容——所需 CLI 版本、筛选器、SDK 和兼容 Loki 的读取 API——请参阅上级 neon 技能中的 可观测性 部分。
Neon 文档
Neon 文档是事实来源,而对象存储正在快速发展,因此请始终以官方文档为准。为获取 Markdown 格式,向任意文档页面的 URL 末尾添加 .md,或请求 Accept: text/markdown。请从文档索引(https://neon.com/docs/llms.txt)和更新日志公告中查找合适的页面。
延伸阅读
- https://neon.com/docs/get-started/backend-overview.md
- https://neon.com/docs/storage/overview.md
- https://neon.com/docs/storage/get-started.md
- https://neon.com/docs/storage/buckets.md
- https://neon.com/docs/storage/objects.md
- https://neon.com/docs/storage/authentication.md
- https://neon.com/docs/storage/s3-compatibility.md
- https://neon.com/docs/storage/troubleshooting.md
- https://files-sdk.dev — Files SDK 文档(
neon适配器)