EasyStarter logoEasyStarter

存储服务

配置 Cloudflare R2 对象存储,管理用户上传的头像与文件

存储服务

EasyStarter 使用 Cloudflare R2 作为对象存储服务,用于上传和管理用户文件。目前内置支持以下两种上传类型:

上传类型说明文件大小限制
avatar用户头像5 MB
attachment附件文件(图片、PDF、文本)25 MB

创建 R2 Bucket

官方文档:R2 Getting started

方式一:通过 Cloudflare Dashboard

  1. 登录 Cloudflare Dashboard
  2. 进入 R2 Object Storage
  3. 点击 Create bucket
  4. 输入 bucket 名称,例如 your-app-bucket
  5. 选择地域(推荐 Automatic)
  6. 点击 Create bucket

方式二:通过 Wrangler CLI

pnpm wrangler r2 bucket create your-app-bucket

配置 wrangler.jsonc

apps/server/wrangler.jsoncr2_buckets 字段中填入你的 bucket 名称:

apps/server/wrangler.jsonc
"r2_buckets": [
  {
    "binding": "STORAGE",
    "bucket_name": "your-app-bucket"
  }
],
  • binding 固定为 STORAGE,这是 Worker 内部访问 R2 的变量名,不要修改
  • bucket_name 改为你在 R2 创建的 bucket 名称

开启公开访问并获取 R2_PUBLIC_URL

文件上传后,需要通过公开 URL 访问。推荐使用 R2 的 Public Access 功能。

通过 Cloudflare Dashboard 开启(推荐)

  1. 进入你的 R2 bucket 详情页
  2. 点击 Settings 标签
  3. Public Access 区域点击 Allow Access
  4. 开启后会自动生成一个公开 URL,格式为:https://pub-xxxxxxxx.r2.dev

这个 URL 就是 R2_PUBLIC_URL

自定义域名(可选)

也可以在 bucket Settings → Custom Domains 绑定自定义域名,例如 https://cdn.yourdomain.com,绑定后使用自定义域名作为 R2_PUBLIC_URL

填入 R2_PUBLIC_URL 环境变量

R2_PUBLIC_URL 需要填入两个环境变量文件:

本地开发apps/server/.dev.vars):

apps/server/.dev.vars
R2_PUBLIC_URL=https://pub-xxxxxxxx.r2.dev

生产部署apps/server/.env.production):

apps/server/.env.production
R2_PUBLIC_URL=https://pub-xxxxxxxx.r2.dev

.env.production 用于通过 pnpm run secrets:bulk:production 批量推送到 Cloudflare Workers 的 Secrets,不会直接参与构建。

配置存储参数(可选)

存储相关参数在 packages/app-config/src/app-config.tscommon.storage 字段中定义,可按需调整:

packages/app-config/src/app-config.ts
storage: {
  provider: "r2",
  publicPath: "/api/storage",   // 文件访问的 API 路径前缀
  keyPrefixes: {
    avatar: "avatars",           // 头像文件存储路径前缀
    attachment: "attachments",   // 附件文件存储路径前缀
  },
  fallbackPrefix: "files",       // 未指定类型时的兜底前缀
  allowedTypes: {
    avatar: ["image/jpeg", "image/png", "image/gif", "image/webp"],
    attachment: ["image/jpeg", "image/png", "image/gif", "image/webp",
                 "application/pdf", "text/plain"],
  },
  maxFileSizes: {
    avatar: 5 * 1024 * 1024,      // 5 MB
    attachment: 25 * 1024 * 1024, // 25 MB
  },
},

扩展其他存储服务

EasyStarter 的存储层基于 StorageProvider 接口设计,只需四步即可接入任意存储服务(如 AWS S3Cloudflare R2MinIO 等)。

第一步:扩展服务商类型

假设以 S3 为例,在 packages/app-config/src/types.ts 中,将新服务商 key 追加到 SUPPORTED_STORAGE_PROVIDERS

packages/app-config/src/types.ts
export const SUPPORTED_STORAGE_PROVIDERS = ["r2", "s3"] as const;

第二步:实现 Provider

apps/server/src/storage/providers/ 下新建文件,实现 StorageProvider 接口:

apps/server/src/storage/providers/s3.ts
import type { StorageProvider } from "../types";

export function createS3StorageProvider({ client, bucket }: {
  client: S3Client;
  bucket: string;
}): StorageProvider {
  return {
    async put(key, data, options) {
      // 调用 S3 SDK 上传文件
    },
    async get(key) {
      // 调用 S3 SDK 下载文件
    },
    async head(key) {
      // 调用 S3 SDK 获取元数据
    },
    async delete(key) {
      // 调用 S3 SDK 删除文件
    },
  };
}

第三步:注册到存储服务商中

apps/server/src/storage/index.tsproviders 对象中注册新 Provider:

apps/server/src/storage/index.ts
import { createS3StorageProvider } from "./providers/s3";

const providers: Record<StorageProviderKey, StorageProvider> = {
  r2: createR2StorageProvider({ bucket: storage }),
  s3: createS3StorageProvider({ client: s3Client, bucket: "your-bucket" }),
};

第四步:切换配置

packages/app-config/src/app-config.ts 中将 storage.provider 改为新服务商的 key:

packages/app-config/src/app-config.ts
storage: {
  provider: "s3", // 切换到新服务商
  // ...其余配置保持不变
},

完成后,所有文件上传、下载、删除操作都会自动通过新 Provider 执行,无需修改业务代码。