存储
使用拆分包架构配置文件存储
LaunchSaaS 通过拆分包设计提供文件存储支持。核心包 @launchsaas/storage 定义接口和 Storage 服务;每个提供商是独立的包,按需安装。
更新时间:2026-03-15
架构
@launchsaas/storage ← 接口 + Storage 类(始终需要)
@launchsaas/storage-s3 ← S3 兼容(AWS S3、Cloudflare R2、MinIO 等)你只需添加实际使用的提供商包。
LaunchSaaS 默认使用
@launchsaas/storage-s3。在你自己的应用中,按需安装该包;若不需要存储功能,跳过
Storage.init() 调用即可。
设置存储功能
1. 安装提供商包
# S3 兼容(AWS S3、Cloudflare R2、MinIO 等)
pnpm add @launchsaas/storage-s32. 添加环境变量
将提供商的 keys 导出添加到应用的 env.ts:
// src/env.ts
import { keys as storageKeys } from "@launchsaas/storage-s3";
export const env = createEnv({
extends: [
// ... 其他 keys
storageKeys,
],
// ...
});然后在 .env 中设置变量:
S3_REGION="auto"
S3_BUCKET="your-bucket-name"
S3_ACCESS_KEY_ID="your-access-key-id"
S3_SECRET_ACCESS_KEY="your-secret-access-key"
S3_API_ENDPOINT="https://<account-id>.r2.cloudflarestorage.com"
S3_PUBLIC_ENDPOINT="https://pub-<id>.r2.dev"3. 在 instrumentation.ts 中初始化
取消注释 src/instrumentation.ts 中的存储代码块:
// src/instrumentation.ts
export async function register() {
if (process.env.NEXT_RUNTIME === "nodejs") {
const { Storage } = await import("@launchsaas/storage");
const { S3StorageProvider } = await import("@launchsaas/storage-s3");
Storage.init(S3StorageProvider.create());
}
}环境变量
| 变量 | 描述 |
|---|---|
S3_REGION | 区域(例如 us-east-1,R2 使用 auto) |
S3_BUCKET | 存储桶名称 |
S3_ACCESS_KEY_ID | 访问密钥 ID |
S3_SECRET_ACCESS_KEY | 秘密访问密钥 |
S3_API_ENDPOINT | S3 API 端点 URL |
S3_PUBLIC_ENDPOINT | 访问上传文件的公共基础 URL |
提供商配置
Cloudflare R2(推荐)
- 前往 Cloudflare Dashboard → R2
- 创建存储桶
- 创建具有 R2 权限的 API 令牌
- 添加上述环境变量
AWS S3
S3_REGION="us-east-1"
S3_BUCKET="your-bucket-name"
S3_ACCESS_KEY_ID="your-access-key-id"
S3_SECRET_ACCESS_KEY="your-secret-access-key"
S3_API_ENDPOINT="https://s3.us-east-1.amazonaws.com"
S3_PUBLIC_ENDPOINT="https://your-bucket-name.s3.us-east-1.amazonaws.com"MinIO(自托管)
S3_REGION="us-east-1"
S3_BUCKET="your-bucket-name"
S3_ACCESS_KEY_ID="your-access-key"
S3_SECRET_ACCESS_KEY="your-secret-key"
S3_API_ENDPOINT="https://your-minio-server.com"
S3_PUBLIC_ENDPOINT="https://your-minio-server.com/your-bucket-name"CORS 配置
对于浏览器上传,在存储桶上配置 CORS:
[
{
"AllowedOrigins": ["https://yourdomain.com"],
"AllowedMethods": ["GET", "PUT", "POST", "DELETE"],
"AllowedHeaders": ["*"],
"MaxAgeSeconds": 3600
}
]自定义存储提供商
要使用内置 S3 提供商未覆盖的存储服务,可创建新的 storage-xxx 包:
1. 创建包
mkdir -p packages/storage-myservice/srcpackages/storage-myservice/package.json:
{
"name": "@launchsaas/storage-myservice",
"version": "0.1.0",
"private": true,
"main": "./src/index.ts",
"exports": {
".": "./src/index.ts"
},
"dependencies": {
"@launchsaas/errors": "workspace:*",
"@launchsaas/storage": "workspace:*",
"@t3-oss/env-nextjs": "^0.13.10",
"my-storage-sdk": "^1.0.0",
"zod": "^4.0.0"
}
}packages/storage-myservice/src/provider.ts:
import type {
DeleteOptions,
StorageProvider,
UploadOptions,
} from "@launchsaas/storage";
export class MyServiceStorageProvider implements StorageProvider {
readonly name = "myservice";
static create(): MyServiceStorageProvider {
return new MyServiceStorageProvider();
}
async upload(options: UploadOptions): Promise<{ url: string }> {
// 实现上传
return { url: "https://example.com/file" };
}
async delete(options: DeleteOptions): Promise<void> {
// 实现删除
}
getPublicUrl(key: string): string {
return `https://example.com/${key}`;
}
getKeyFromUrl(url: string): string {
return url.replace("https://example.com/", "");
}
}packages/storage-myservice/src/index.ts:
export { MyServiceStorageProvider } from "./provider";
export { keys } from "./keys";2. 在应用中接入
pnpm add @launchsaas/storage-myservice将 keys 添加到 env.ts,并在 instrumentation.ts 中初始化:
export async function register() {
const { Storage } = await import("@launchsaas/storage");
const { MyServiceStorageProvider } =
await import("@launchsaas/storage-myservice");
Storage.init(MyServiceStorageProvider.create());
}禁用存储功能
若要完全禁用存储功能,只需在 instrumentation.ts 中不调用 Storage.init()。Storage.enabled() 将返回 false,操作会快速失败并给出清晰的错误信息。