ReboFilesDocs
Empezar3
Qué es ReboFiles
Primeros pasos
Demos para ejecutar
Ponlo en tu app6
Componente de React
Headless: tu propia interfaz
Sin framework
Estilos y temas
Teclado y accesibilidad
Idiomas
Archivos9
Subidas
Vistas previas y el visor
Búsqueda
Cambios en vivo y carpetas grandes
Comentarios
Destacados y campos
Versiones, papelera y espacio
Descargar como .zip
Documentos de Word
El servidor14
La ruta
Frameworks e inicio de sesión
Quién puede hacer qué
Almacenamiento: S3, GCS, Azure y disco
Revisar las subidas
Metadatos: SQLite y Postgres
Esquema de la base de datos
Compartir y enlaces
Actividad y notificaciones
Ganchos, registros y límites
Retención, bloqueos y exportación
Escritorio y otros orígenes
Sincronizar una carpeta
Ir a producción
En equipo1
Asistentes de IA (MCP)
Referencia5
Paquetes
Componentes y hooks
API del cliente
Rutas HTTP
Estado y hoja de ruta
↑↓ para moverte↵ para abrirEsc para cerrar38 páginas

El servidor Almacenamiento: S3, GCS, Azure y disco

Almacenamiento: S3, GCS, Azure y disco

Los bytes van del navegador directo al bucket y de vuelta, a direcciones que el servidor firma durante unos minutos. Eso le pide dos cosas al bucket: una clave con la que firmar, y una regla que deje entrar a tus páginas.
S3

La clave

En Cloudflare R2: R2 → Create bucket, luego Manage API tokens → Create API token con “Object Read & Write” solo para ese bucket.

.env
# The S3 API of the account, not the public address of the bucket (r2.dev)
S3_ENDPOINT=https://<account-id>.r2.cloudflarestorage.com
S3_BUCKET=acme-files
S3_REGION=auto
S3_ACCESS_KEY_ID=…
S3_SECRET_ACCESS_KEY=…
# Optional: before every key, to share the bucket with something else
S3_PREFIX=files/
# What tokens are signed with: long, random, the same in every process
REBOFILES_SECRET=…

s3Storage(s3OptionsFrom(process.env)) las lee. En AWS, omite S3_ENDPOINT y da la región; MinIO y Backblaze usan las mismas variables.

CORS

La regla de orígenes

En Settings → CORS policy, con las direcciones desde las que se sirve tu aplicación. ETag tiene que estar expuesto.

CORS policy
[
  {
    "AllowedOrigins": ["https://your-app.com", "http://localhost:3000"],
    "AllowedMethods": ["GET", "PUT"],
    "AllowedHeaders": ["content-type"],
    "ExposeHeaders": ["ETag"],
    "MaxAgeSeconds": 3600
  }
]

Una regla de ciclo de vida que aborta las subidas multiparte sin terminar tras un día evita que las partes de una subida nunca terminada se queden en el bucket.

GCS

Google Cloud Storage

Por su API XML, que habla S3: las mismas direcciones, firmadas con una clave HMAC del bucket.

import { gcsOptionsFrom, gcsStorage } from '@rebofiles/server';

// An HMAC key of the bucket: Cloud Storage → Settings → Interoperability.
// GCS_BUCKET, GCS_ACCESS_KEY_ID (GOOG1…), GCS_SECRET_ACCESS_KEY, GCS_PREFIX
createDriveHandler({ ...options, storage: gcsStorage(gcsOptionsFrom(process.env)) });

Lo que esa API no hace se hace de otra forma: cada clave se borra por separado, y se envían todas las partes de una versión nueva, porque no copia ninguna. El CORS del bucket, puesto con gcloud storage buckets update --cors-file, tiene que exponer ETag.

Azure

Azure Blob Storage

Un contenedor de una cuenta de almacenamiento: direcciones firmadas con la clave de la cuenta (una SAS de cada blob, por minutos), cada parte un bloque, y el archivo hecho de su lista de bloques.

import { azureOptionsFrom, azureStorage } from '@rebofiles/server';

// AZURE_STORAGE_ACCOUNT, AZURE_STORAGE_KEY, AZURE_STORAGE_CONTAINER, AZURE_STORAGE_PREFIX
createDriveHandler({ ...options, storage: azureStorage(azureOptionsFrom(process.env)) });
  • Subidas retomadas a partir de los bloques que lista Azure, y una versión nueva enviada solo con las partes que cambiaron (Put Block From URL).
  • Los bloques de una subida que nunca terminó se van solos a la semana: no hay nada que barrer.
  • El CORS de la cuenta (Uso compartido de recursos) deja entrar los orígenes de tus páginas, con PUT, GET y HEAD; no hace falta exponer ninguna cabecera, porque los bloques se juntan por su número.
  • Probado contra Azurite, el emulador de Microsoft, en los tests del servidor.
Disco

En el disco

diskStorage({ folder, secret }) guarda los bytes en una carpeta, y el propio servidor los transporta, a direcciones suyas: para una sola máquina, una demo o un test.

Errores

Cuando algo falla

S3_ENDPOINT is the public address of the bucket (r2.dev)

La API S3 de R2 está en https://<account-id>.r2.cloudflarestorage.com, en la página de ajustes del bucket.

The part could not be sent. If the bytes go to a bucket, check that it lets this origin in.

La regla de orígenes: falta la dirección de la página en AllowedOrigins, o content-type en AllowedHeaders.

The bucket did not let the page read the ETag of a part.

Los archivos pequeños suben y los grandes no: ExposeHeaders necesita ETag.

The bucket answered 403 to …

La clave: no puede escribir en ese bucket, o el endpoint, la región o el nombre del bucket no son para los que se hizo.

There is no room for it: … are used, and it is ….

La cuota de la persona (413 over-quota): más espacio en los ajustes del drive, o menos en sus archivos y su papelera.

En el repositorio, npm run storage:check hace todo el recorrido de un archivo contra el bucket de apps/storage/.env y dice qué respondió el bucket en el paso que falló.