EmDash almacena los medios cargados (imágenes, documentos, videos) en un backend de almacenamiento configurable. Elija según su plataforma de despliegue y requisitos.
Visión General
| Almacenamiento | Mejor Para | Características |
|---|---|---|
| R2 Binding | Cloudflare Workers | Cero configuración, rápido |
| S3 | Cualquier plataforma | Cargas firmadas, soporte CDN |
| Local | Desarrollo | Almacenamiento simple de archivos |
Cloudflare R2 (Binding)
Use R2 bindings al desplegar en Cloudflare Workers para la integración más rápida.
import emdash from "emdash/astro";
import { r2 } from "@emdash-cms/cloudflare";
export default defineConfig({
integrations: [
emdash({
storage: r2({ binding: "MEDIA" }),
}),
],
});
Configuración
| Opción | Tipo | Descripción |
|---|---|---|
binding | string | Nombre del binding R2 de wrangler.jsonc |
publicUrl | string | URL pública opcional para el bucket |
Configuración Inicial
Agregue el binding R2 a su configuración de Wrangler:
wrangler.jsonc
{
"r2_buckets": [
{
"binding": "MEDIA",
"bucket_name": "emdash-media"
}
]
} wrangler.toml
[[r2_buckets]]
binding = "MEDIA"
bucket_name = "emdash-media" Acceso Público
Para URLs de medios públicas, habilite el acceso público en su bucket R2:
- Vaya al Panel de Cloudflare > R2 > su bucket
- Habilite el acceso público en Configuración
- Agregue la URL pública a su configuración:
storage: r2({
binding: "MEDIA",
publicUrl: "https://pub-xxxx.r2.dev",
});
Almacenamiento Compatible con S3
El adaptador S3 funciona con Cloudflare R2 (vía API S3), MinIO y otros servicios compatibles con S3.
La siguiente configuración apunta EmDash a un bucket compatible con S3:
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
}),
}),
],
});
Configuración
| Opción | Tipo | Requerido | Descripción |
|---|---|---|---|
endpoint | string | sí | URL del endpoint S3 |
bucket | string | sí | Nombre del bucket |
accessKeyId | string | no* | Clave de acceso |
secretAccessKey | string | no* | Clave secreta |
region | string | no | Región (predeterminado: "auto") |
publicUrl | string | no | CDN opcional o URL pública |
* Tanto accessKeyId como secretAccessKey deben proporcionarse juntas, o ambas omitirse.
Resolución de configuración S3 desde variables de entorno
Cualquier campo omitido de s3({...}) se lee de la variable de entorno S3_* correspondiente cuando el proceso se inicia. Esto le permite construir una imagen de contenedor una vez e inyectar credenciales en el arranque sin reconstrucción. Los valores explícitos en s3({...}) siempre tienen precedencia sobre las variables de entorno.
| Variable de entorno | Campo | Notas |
|---|---|---|
S3_ENDPOINT | endpoint | Debe ser una URL http/https válida |
S3_BUCKET | bucket | |
S3_ACCESS_KEY_ID | accessKeyId | |
S3_SECRET_ACCESS_KEY | secretAccessKey | |
S3_REGION | region | Predeterminado: "auto" |
S3_PUBLIC_URL | publicUrl | Prefijo CDN opcional |
Las variables de entorno se leen de process.env cuando el proceso se inicia. Esta es una característica solo de Node.
Llamar a s3() sin argumentos lee cada campo de las variables de entorno S3_*:
import emdash, { s3 } from "emdash/astro";
export default defineConfig({
integrations: [
emdash({
// s3() sin argumentos: todos los campos de variables de entorno S3_*
storage: s3(),
// O mezclado: sobrescribir un campo, resto del entorno
// storage: s3({ publicUrl: "https://cdn.example.com" }),
}),
],
});
R2 vía API S3
Use credenciales S3 con R2 para características como URLs de carga firmadas:
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",
});
Genere credenciales de API R2 en el panel de Cloudflare bajo R2 > Manage R2 API Tokens.
MinIO
Apunte el adaptador S3 a un endpoint MinIO con sus credenciales de acceso:
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",
});
Sistema de Archivos Local
Use almacenamiento local para desarrollo. Los archivos se almacenan en un directorio en disco.
import emdash, { local } from "emdash/astro";
export default defineConfig({
integrations: [
emdash({
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
}),
],
});
Configuración
| Opción | Tipo | Descripción |
|---|---|---|
directory | string | Ruta del directorio para almacenamiento de archivos |
baseUrl | string | URL base para servir archivos |
La baseUrl debe coincidir con el endpoint de archivos de medios de EmDash (/_emdash/api/media/file) a menos que configure un servidor de archivos estáticos personalizado.
Configuración Basada en Entorno
Cambie backends de almacenamiento según el entorno:
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 })],
});
Cargas Firmadas
El adaptador S3 soporta URLs de carga firmadas, permitiendo a los clientes cargar directamente al almacenamiento sin pasar por su servidor. Esto mejora el rendimiento para archivos grandes.
Las cargas firmadas son automáticas al usar el adaptador S3. La interfaz de administración las usa cuando están disponibles.
Adaptadores que soportan cargas firmadas:
- S3 (incluyendo R2 vía API S3)
Adaptadores que no soportan cargas firmadas:
- R2 binding (use el adaptador S3 con credenciales R2 en su lugar)
- Local
Interfaz de Storage
Todos los adaptadores de almacenamiento implementan la misma interfaz:
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;
}
Esta consistencia permite cambiar backends de almacenamiento sin cambiar el código de la aplicación.