Elige un adaptador de almacenamiento para medios subidos. Un respaldo de base de datos contiene metadatos de medios, no los archivos almacenados, así que respalda el backend de almacenamiento por separado.
Resumen
| Almacenamiento | Úsalo cuando | Uploads firmados |
|---|---|---|
| R2 binding | El sitio corre en Cloudflare Workers | No |
| S3 | Un sitio Node.js usa AWS S3, la API S3 de R2, MinIO o almacenamiento compatible | Sí |
| Local | Un sitio Node.js tiene un volumen persistente escribible | No |
Cloudflare R2 binding
Usa el adaptador de binding R2 en Cloudflare Workers. El binding proporciona acceso en tiempo de ejecución, por lo que el sitio no necesita claves de acceso R2.
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
Agrega el binding R2 a tu 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 servir medios desde un bucket público, conecta un dominio personalizado con la API de Cloudflare, luego establece su origen como publicUrl. La URL de desarrollo r2.dev de Cloudflare tiene límite de velocidad y no está diseñada para tráfico de producción.
storage: r2({
binding: "MEDIA",
publicUrl: "https://media.example.com",
});
Si el mismo bucket almacena respaldos JSON automáticos, un origen de bucket público puede exponer objetos bajo backups/. Usa un bucket privado y la ruta de medios de EmDash, o restringe el origen público a objetos de medios. Ver Respaldos.
Almacenamiento compatible con S3
El adaptador S3 funciona en Node.js con la API S3 de Cloudflare R2, AWS S3, MinIO y servicios compatibles.
La siguiente configuración resuelve el endpoint, bucket, credenciales, región y URL pública opcional desde variables S3_* cuando el proceso Node.js inicia:
import emdash, { s3 } from "emdash/astro";
export default defineConfig({
integrations: [
emdash({
storage: s3(),
}),
],
});
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 (defecto: "auto") |
publicUrl | string | no | CDN o URL pública opcional |
* Tanto accessKeyId como secretAccessKey deben proporcionarse juntos, o ambos omitirse.
Resolver configuración S3 desde variables de entorno
Cualquier campo omitido de s3({...}) se lee de la variable de entorno S3_* correspondiente cuando el proceso inicia. Esto te permite construir una imagen de contenedor una vez e inyectar credenciales al inicio sin reconstruir. Los valores explícitos en s3({...}) siempre tienen prioridad 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 | Por defecto "auto" |
S3_PUBLIC_URL | publicUrl | Prefijo CDN opcional |
Las variables de entorno se leen de process.env cuando el proceso inicia. Esta es una función 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 desde variables de entorno S3_*
storage: s3(),
// O mezclar: sobrescribir un campo, el resto desde el entorno
// storage: s3({ publicUrl: "https://cdn.example.com" }),
}),
],
});
R2 vía API S3
Usa el adaptador S3 en Node.js cuando se requieren uploads firmados directos a R2. Crea credenciales API R2 con alcance limitado con la API o CLI de Cloudflare, luego establece las siguientes variables de ejecución:
S3_ENDPOINT=https://<account-id>.r2.cloudflarestorage.com
S3_BUCKET=emdash-media
S3_ACCESS_KEY_ID=<r2-access-key-id>
S3_SECRET_ACCESS_KEY=<r2-secret-access-key>
S3_REGION=auto
S3_PUBLIC_URL=https://media.example.com
Guarda los valores reales en el gestor de secretos de la plataforma de hosting Node.js. La URL pública es opcional y no reemplaza el endpoint de la API S3 usado para uploads.
MinIO
Apunta las mismas variables de ejecución a MinIO. Establece S3_ENDPOINT al origen de la API MinIO, S3_BUCKET al nombre del bucket y las dos variables de credenciales a una clave de acceso MinIO con alcance limitado. Establece S3_PUBLIC_URL solo cuando ese origen sirve los objetos del bucket públicamente.
Sistema de archivos local
Usa almacenamiento local para desarrollo o un servidor Node.js único con disco persistente. Los archivos se almacenan en un directorio en ese 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 almacenar archivos |
baseUrl | string | URL base para servir archivos |
La baseUrl debe coincidir con el endpoint de archivo de medios de EmDash (/_emdash/api/media/file) a menos que configures un servidor de archivos estáticos personalizado.
Usar almacenamiento separado para entornos separados
La siguiente configuración usa un directorio local durante el desarrollo y R2 en el build de producción de Cloudflare:
import emdash, { 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 })],
});
Uploads firmados
El adaptador S3 soporta URLs de upload firmadas, permitiendo a los clientes subir directamente al almacenamiento sin pasar por tu servidor. Esto mejora el rendimiento para archivos grandes.
Los uploads firmados son automáticos al usar el adaptador S3. La interfaz admin los usa cuando están disponibles.
Adaptadores que soportan uploads firmados:
- S3 (incluyendo R2 vía API S3)
Adaptadores que no soportan uploads firmados:
- R2 binding (usa el adaptador S3 con credenciales R2 en su lugar)
- Local
Interfaz de almacenamiento
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.