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" }),
}),
],
});
配置
| 选项 | 类型 | 说明 |
|---|---|---|
binding | string | wrangler.jsonc 中的 R2 绑定名 |
publicUrl | string | 可选的存储桶公开 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 存储桶上启用公开访问:
- Cloudflare 控制台 > R2 > 你的存储桶
- 在设置中启用公开访问
- 将公开 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
}),
}),
],
});
配置
| 选项 | 类型 | 必填 | 说明 |
|---|---|---|---|
endpoint | string | 是 | S3 端点 URL |
bucket | string | 是 | 存储桶名称 |
accessKeyId | string | 否* | 访问密钥 |
secretAccessKey | string | 否* | 秘密密钥 |
region | string | 否 | 区域(默认 "auto") |
publicUrl | string | 否 | 可选 CDN 或公开 URL |
* accessKeyId 和 secretAccessKey 必须同时提供,或同时省略。
从环境变量解析 S3 配置
s3({...}) 中省略的字段会在进程启动时从对应的 S3_* 环境变量读取。这样你可以
一次构建容器镜像,启动时注入凭证,无需重新构建。s3({...}) 中显式传入的值始终
优先于环境变量。
| 环境变量 | 字段 | 备注 |
|---|---|---|
S3_ENDPOINT | endpoint | 必须是合法的 http/https URL |
S3_BUCKET | bucket | |
S3_ACCESS_KEY_ID | accessKeyId | |
S3_SECRET_ACCESS_KEY | secretAccessKey | |
S3_REGION | region | 默认 "auto" |
S3_PUBLIC_URL | publicUrl | 可选 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",
}),
}),
],
});
配置
| 选项 | 类型 | 说明 |
|---|---|---|
directory | string | 文件存储目录 |
baseUrl | string | 对外提供文件的基础 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;
}
接口一致,便于在不改应用代码的情况下切换后端。