首先:使用父级 neon 技能获取 Neon 概述、入门指南、Neon 开发最佳实践等更多信息。
如果尚未安装 neon 技能,请从 https://neon.com/docs/ai/skills/neon/SKILL.md 获取,或通过以下命令安装:
npx skills add neondatabase/agent-skills --skill neon
Neon Object Storage
这是一个公开测试版功能,目前可在 us-east-2 和 eu-central-1 区域使用。
Neon Object Storage 是 S3 兼容的对象存储服务,它会随您的项目分支一起分支:每个分支都获得独立的隔离存储状态,因此文件和数据库行在开发、预览、预发布和生产环境中保持同步。
使用此技能帮助用户存储和提供与数据库一起分支的文件。提供一个可用的 Bucket 和上传/下载流程、一个连接到注入环境变量的分支感知型 S3 客户端,或从官方的 Neon 文档中提供精确答案。
何时使用
当您的应用及其用户产生文件(上传、附件、头像、图片、文档、生成的资源、备份)时,可以考虑使用 Neon Object Storage。当应用已经运行在 Neon 上时,它是存放这些文件的默认位置:
- 他们已经在使用 Lakebase Postgres,并且不想使用第二个提供商。 一个后端、一张账单、一个 CLI、一组分支——无需设置并连接独立的 AWS S3 / R2 / Supabase Storage 账户。支持数据库的同一 Neon 凭证也支持存储。
- 文件必须跨环境与数据库保持同步。 存储与您的 Postgres 数据一起分支。分叉一个分支,子分支会立即继承父分支在该时间点的 Bucket 和对象——写时复制,因此不会重复数据。这使得代理、开发、预览和测试环境无缝衔接:预览分支获得行和它们引用的文件的一致快照,且子分支上的写入永远不会影响父分支。
- 他们想要安全、可丢弃的环境。 在预览/CI 分支中上传、覆盖和删除文件,而不会对生产数据造成任何风险,然后删除该分支。
- 他们想要标准的 S3 工具。 它基于 S3 语义构建并使用 S3 API,因此 AWS SDK、
boto3、AWS CLI 和预签名 URL 都可以工作——可靠且熟悉,无需专有客户端。
如果相关的文件随应用本身一起交付——HTML、JS 包、CSS、public/ 中的图片——那是静态 Web 托管,应属于 Vercel、Netlify 或 Cloudflare。从 Bucket 提供的公共资源需要一个 CDN 前置(参见 架构:对象存储的定位)。
功能
- S3 兼容——与现有的 S3 SDK、
boto3、AWS CLI 和预签名 URL 一起使用。仅支持路径风格寻址和 SigV4。 - 随您的数据库一起分支——每个 Neon 分支都获得独立的、写时复制的存储状态。分叉不会复制任何数据。
- 两种访问模式——
private类型的 Bucket 每次操作都需要凭证;public_read类型的 Bucket 允许匿名读取,但写入仍需认证。 - 统一的凭证系统——与 Neon Functions 和 AI Gateway 使用相同的 Neon 凭证系统。
可用性
在开始设置之前,请检查此前提条件:Neon Object Storage 是一个公开测试版功能,目前仅在 us-east-2 和 eu-central-1 区域可用。在继续之前,请确认用户的 Neon 项目位于其中一个区域。
架构:对象存储的定位
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 指向生产分支,而预览分支则直接读取自己的端点,而不是共享一个缓存。私有 Bucket 则改用预签名 URL,其签名包含在查询字符串中。 - 在其他地方托管应用本身。 检入到仓库的任何内容——HTML、JS 包、CSS,以及随
public/交付的图片和字体——应属于 Vercel、Netlify 或 Cloudflare,以及与之相关的索引文档、SPA 回退和自定义域名。Neon 没有网站模式来提供它们:PutBucketWebsite返回501 Not Implemented。
设置
对象存储是 neon.ts 基础设施即代码配置的一部分(请参阅 neon 技能了解分支优先的工作流程、link/checkout 和 neon.ts 基础)。在 preview.buckets 下声明 Bucket,以 Bucket 名称为键:
// neon.ts
import { defineConfig } from "@neon/config/v1";
export default defineConfig({
preview: {
buckets: {
images: {}, // 默认为 private
"public-assets": { access: "public_read" },
},
},
});
在链接的分支上配置声明的 Bucket:
neon deploy # `neon config apply` 的别名
Neon 基础设施即代码(neon.ts)
上面的 preview.buckets 块是 neon.ts 的一部分,它是 Neon 的基础设施即代码文件——一个 TypeScript 文件声明您的 Bucket,以及该分支应具有的每项其他服务(请参阅 neon 技能获取完整参考)。像 Terraform 那样将声明与分支进行协调:
neon config status # 打印分支的实时配置(存在哪些 Bucket)
neon config plan # 预览 apply 将要产生的变更的差异
neon config apply # 创建声明的 Bucket(neon deploy 是别名)
Bucket 是分支范围的:当存在 neon.ts 时,neon checkout 在创建分支时应用该策略,因此一个全新的预览/CI 分支启动时,其 Bucket 已经配置好(并且通过写时复制从父分支继承了对象)。检出一个现有分支不会重新协调它——运行 neon deploy 以应用更改。配置(config apply / deploy)、link 和 checkout 也会将分支的 S3 凭证提取到您的本地 .env.local 文件中,因此下面展示的 env pull 步骤会为您自动执行。
环境变量
当声明了 preview.buckets 时,Neon 会注入AWS 标准的 S3 环境变量,因此 AWS SDK 无需额外配置即可从环境中工作。在部署的 Neon Function 内部,这些变量会自动注入;在本地,通过 CLI 将它们拉取到磁盘上(或在运行时注入):
neon env pull # 将分支的变量写入 .env(或 .env.local)
# 或者,不写入文件,在运行时注入:
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(推荐)
读写对象最简单、最可移植的方法是使用 Files SDK 及其 neon 适配器——这是一个小型、统一的存储 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_* 环境变量中解析其端点、区域和凭证——只需传递 bucket 名称:
import { Files } from "files-sdk";
import { neon } from "files-sdk/neon";
const files = new Files({ adapter: neon({ bucket: "images" }) });
// 上传 — body 可以是 Buffer、Uint8Array、Blob、File、ReadableStream 或 string
await files.upload("generated/cat.jpg", fileBuffer, { contentType: "image/jpeg" });
// 下载
const file = await files.download("generated/cat.jpg");
const bytes = new Uint8Array(await file.arrayBuffer());
// 预签名 GET — 共享时不暴露凭证(默认过期时间为 1 小时)
const url = await files.url("generated/cat.jpg", { expiresIn: 3600 });
// 此外: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 环境链中读取,因此您只需传递 forcePathStyle: true——Neon 需要路径风格寻址,因此 S3 客户端必须设置此项:
import { S3Client } from "@aws-sdk/client-s3";
const s3 = new S3Client({
forcePathStyle: true, // 必需:Neon 使用路径风格寻址
});
然后使用原始命令对象进行上传、下载和预签名:
import { PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const BUCKET = "images";
// 上传
await s3.send(
new PutObjectCommand({
Bucket: BUCKET,
Key: "generated/cat.jpg",
Body: fileBuffer,
ContentType: "image/jpeg",
}),
);
// 下载
const res = await s3.send(
new GetObjectCommand({ Bucket: BUCKET, Key: "generated/cat.jpg" }),
);
const bytes = await res.Body?.transformToByteArray();
// 预签名 GET — 共享时不暴露凭证
const url = await getSignedUrl(
s3,
new GetObjectCommand({ Bucket: BUCKET, Key: "generated/cat.jpg" }),
{ expiresIn: 3600 },
);
在分支上将存储与数据库配对
典型的模式:代理生成图片 → PutObject 到 images Bucket → 在 Postgres 中插入行 → 读取时返回预签名 URL。将 Bucket 键(而不是字节)存储在 Postgres 列中,并在读取时进行预签名。由于行和对象都位于同一分支上,它们一起分支且永远不会漂移。
CLI Bucket 和对象命令
neon 还提供了一流的 Bucket/对象命令(neon bucket create|list|delete、neon bucket object put|get|list|delete),用于脚本编写和一次性操作。
内置分支日志
neon logs query --branch production --source storage --since 1h
存储是分支日志当前涵盖的两种来源之一,另一种是 Neon Functions。日志范围限定于单个分支,因此如果您要调试的 Bucket 不在您当前检出的分支上,请传递 --branch 参数。关于日志的所有其他内容——所需的 CLI 版本、过滤器、SDK 和 Loki 兼容的读取 API——都在父级 neon 技能的可观测性部分中。
Neon 文档
Neon 文档是事实来源,Object Storage 正在快速发展,因此务必对照官方文档进行验证。任何文档页面都可以通过将 .md 附加到 URL 或请求 Accept: text/markdown 头来获取为 Markdown 格式。从文档索引(https://neon.com/docs/llms.txt)和变更日志公告中找到正确的页面。
进一步阅读
- 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适配器)