存储选项

本页内容

EmDash 将上传的媒体(图片、文档、视频)保存在可配置的后端,请按平台与需求选择。

概览

存储适用场景特点
R2 绑定Cloudflare Workers零配置、速度快
S3任意平台签名上传、CDN 支持
本地开发简单文件系统存储

Cloudflare R2(绑定)

部署到 Cloudflare Workers 时,使用 R2 绑定可最快集成。

import emdash from "emdash/astro";
import { r2 } from "@emdash-cms/cloudflare";

export default defineConfig({
	integrations: [
		emdash({
			storage: r2({ binding: "MEDIA" }),
		}),
	],
});

配置

选项类型说明
bindingstringwrangler.jsonc 中的 R2 绑定名
publicUrlstring可选的存储桶公开 URL

设置

将 R2 绑定加入 Wrangler 配置:

wrangler.jsonc

{
  "r2_buckets": [
    {
      "binding": "MEDIA",
      "bucket_name": "emdash-media"
    }
  ]
}

wrangler.toml

[[r2_buckets]]
binding = "MEDIA"
bucket_name = "emdash-media"

公开访问

若需公开媒体 URL,请在 R2 存储桶上启用公开访问:

  1. Cloudflare 控制台 > R2 > 你的存储桶
  2. 在设置中启用公开访问
  3. 将公开 URL 写入配置:
storage: r2({
	binding: "MEDIA",
	publicUrl: "https://pub-xxxx.r2.dev",
});

S3 兼容存储

S3 适配器适用于 Cloudflare R2(S3 API)、MinIO 及其他兼容服务。

import emdash, { s3 } from "emdash/astro";

export default defineConfig({
	integrations: [
		emdash({
			storage: s3({
				endpoint: process.env.S3_ENDPOINT,
				bucket: process.env.S3_BUCKET,
				accessKeyId: process.env.S3_ACCESS_KEY_ID,
				secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
				region: "auto", // Optional, defaults to "auto"
				publicUrl: process.env.S3_PUBLIC_URL, // Optional CDN URL
			}),
		}),
	],
});

配置

选项类型必填说明
endpointstringS3 端点 URL
bucketstring存储桶名称
accessKeyIdstring否*访问密钥
secretAccessKeystring否*秘密密钥
regionstring区域(默认 "auto"
publicUrlstring可选 CDN 或公开 URL

* accessKeyIdsecretAccessKey 必须同时提供,或同时省略。

从环境变量解析 S3 配置

s3({...}) 中省略的字段会在进程启动时从对应的 S3_* 环境变量读取。这样你可以 一次构建容器镜像,启动时注入凭证,无需重新构建。s3({...}) 中显式传入的值始终 优先于环境变量。

环境变量字段备注
S3_ENDPOINTendpoint必须是合法的 http/https URL
S3_BUCKETbucket
S3_ACCESS_KEY_IDaccessKeyId
S3_SECRET_ACCESS_KEYsecretAccessKey
S3_REGIONregion默认 "auto"
S3_PUBLIC_URLpublicUrl可选 CDN 前缀

环境变量在进程启动时从 process.env 读取。此功能仅限 Node 环境。

import emdash, { s3 } from "emdash/astro";

export default defineConfig({
	integrations: [
		emdash({
			// s3() with no args: all fields from S3_* environment variables
			storage: s3(),

			// Or mix: override one field, rest from environment
			// storage: s3({ publicUrl: "https://cdn.example.com" }),
		}),
	],
});

通过 S3 API 使用 R2

使用 S3 凭证连接 R2 以获得签名上传 URL 等功能:

storage: s3({
	endpoint: "https://<account-id>.r2.cloudflarestorage.com",
	bucket: "emdash-media",
	accessKeyId: process.env.R2_ACCESS_KEY_ID,
	secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
	publicUrl: "https://pub-xxxx.r2.dev",
});

在控制台 R2 > Manage R2 API Tokens 生成 R2 API 凭证。

MinIO

storage: s3({
	endpoint: "https://minio.example.com",
	bucket: "emdash-media",
	accessKeyId: process.env.MINIO_ACCESS_KEY,
	secretAccessKey: process.env.MINIO_SECRET_KEY,
	publicUrl: "https://minio.example.com/emdash-media",
});

本地文件系统

开发环境可使用本地存储,文件保存在磁盘目录。

import emdash, { local } from "emdash/astro";

export default defineConfig({
	integrations: [
		emdash({
			storage: local({
				directory: "./uploads",
				baseUrl: "/_emdash/api/media/file",
			}),
		}),
	],
});

配置

选项类型说明
directorystring文件存储目录
baseUrlstring对外提供文件的基础 URL

除非使用自定义静态服务器,baseUrl 应与 EmDash 媒体文件端点(/_emdash/api/media/file)一致。

按环境配置

可按环境切换存储后端:

import emdash, { s3, local } from "emdash/astro";
import { r2 } from "@emdash-cms/cloudflare";

const storage = import.meta.env.PROD
	? r2({ binding: "MEDIA" })
	: local({
			directory: "./uploads",
			baseUrl: "/_emdash/api/media/file",
		});

export default defineConfig({
	integrations: [emdash({ storage })],
});

签名上传

S3 适配器支持签名上传 URL,客户端可直接上传到存储,无需经过你的服务器,有利于大文件性能。

使用 S3 适配器时签名上传自动可用;管理界面在支持时会使用。

支持签名上传的适配器:

  • S3(含通过 S3 API 的 R2)

不支持的适配器:

  • R2 绑定(请改用带 R2 凭证的 S3 适配器)
  • 本地

存储接口

所有存储适配器实现相同接口:

interface Storage {
	upload(options: {
		key: string;
		body: Buffer | Uint8Array | ReadableStream;
		contentType: string;
	}): Promise<UploadResult>;

	download(key: string): Promise<DownloadResult>;
	delete(key: string): Promise<void>;
	exists(key: string): Promise<boolean>;
	list(options?: ListOptions): Promise<ListResult>;
	getSignedUploadUrl(options: SignedUploadOptions): Promise<SignedUploadUrl>;
	getPublicUrl(key: string): string;
}

接口一致,便于在不改应用代码的情况下切换后端。