Elegir almacenamiento de medios

En esta página

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 cuandoUploads firmados
R2 bindingEl sitio corre en Cloudflare WorkersNo
S3Un sitio Node.js usa AWS S3, la API S3 de R2, MinIO o almacenamiento compatibleSí
LocalUn sitio Node.js tiene un volumen persistente escribibleNo

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ónTipoDescripción
bindingstringNombre del binding R2 de wrangler.jsonc
publicUrlstringURL 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ónTipoRequeridoDescripción
endpointstringsíURL del endpoint S3
bucketstringsíNombre del bucket
accessKeyIdstringno*Clave de acceso
secretAccessKeystringno*Clave secreta
regionstringnoRegión (defecto: "auto")
publicUrlstringnoCDN 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 entornoCampoNotas
S3_ENDPOINTendpointDebe ser una URL http/https válida
S3_BUCKETbucket
S3_ACCESS_KEY_IDaccessKeyId
S3_SECRET_ACCESS_KEYsecretAccessKey
S3_REGIONregionPor defecto "auto"
S3_PUBLIC_URLpublicUrlPrefijo 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ónTipoDescripción
directorystringRuta del directorio para almacenar archivos
baseUrlstringURL 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.