Opciones de Almacenamiento

En esta página

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

AlmacenamientoMejor ParaCaracterísticas
R2 BindingCloudflare WorkersCero configuración, rápido
S3Cualquier plataformaCargas firmadas, soporte CDN
LocalDesarrolloAlmacenamiento 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ónTipoDescripción
bindingstringNombre del binding R2 de wrangler.jsonc
publicUrlstringURL 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:

  1. Vaya al Panel de Cloudflare > R2 > su bucket
  2. Habilite el acceso público en Configuración
  3. 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ónTipoRequeridoDescripción
endpointstringURL del endpoint S3
bucketstringNombre del bucket
accessKeyIdstringno*Clave de acceso
secretAccessKeystringno*Clave secreta
regionstringnoRegión (predeterminado: "auto")
publicUrlstringnoCDN 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 entornoCampoNotas
S3_ENDPOINTendpointDebe ser una URL http/https válida
S3_BUCKETbucket
S3_ACCESS_KEY_IDaccessKeyId
S3_SECRET_ACCESS_KEYsecretAccessKey
S3_REGIONregionPredeterminado: "auto"
S3_PUBLIC_URLpublicUrlPrefijo 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ónTipoDescripción
directorystringRuta del directorio para almacenamiento de archivos
baseUrlstringURL 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.