Ir al contenido

Almacenamiento y Archivos

El módulo client.storage proporciona métodos para la gestión de archivos — subida, descarga, listado y eliminación. Funciona tanto con disco local como con backends de almacenamiento compatibles con S3, según la configuración de su servidor.

Todos los métodos de almacenamiento utilizan el transporte compartido, por lo que los tokens de autenticación se inyectan automáticamente.

Use putObject() para subir un archivo. Acepta un objeto File o Blob junto con una clave de almacenamiento y metadatos opcionales:

const result = await client.storage.putObject({
file: fileObject, // File or Blob
key: "products/images/camera.jpg", // Storage path (optional)
bucket: "uploads", // Bucket name (optional)
public: false, // Store public (permanent token-less URL) — optional, default false
metadata: { // Custom metadata (optional)
description: "Product photo",
uploadedBy: "user-123"
}
});
// result: { key: string; bucket: string; storageUrl: string }

Un archivo de más de 8 MiB se sube por el endpoint reanudable (TUS) del servidor, 8 MiB por solicitud, en lugar de como una única solicitud multipart que el servidor analiza completa — así que una subida grande cuesta al servidor un fragmento de memoria, no de tres a cinco veces el archivo. Un fragmento que falla por el camino se vuelve a enviar desde donde llegó el servidor. El resultado, el objeto almacenado y las comprobaciones del servidor son los mismos en ambos casos. Cuando el endpoint reanudable no puede aceptar el archivo (un servidor más antiguo, o varias instancias que no comparten subidas), se envía como un único POST /api/storage/upload, y también cada archivo posterior de ese cliente. Consulta Subidas reanudables (TUS).

const input = document.querySelector<HTMLInputElement>("#file-input");
const file = input?.files?.[0];
if (file) {
const result = await client.storage.putObject({
file,
key: `avatars/${userId}/${file.name}`
});
console.log("Uploaded to:", result.key);
}

Recupere una URL de descarga y los metadatos de un archivo almacenado:

const { url, metadata, fileNotFound } = await client.storage.getSignedUrl(
"products/images/camera.jpg"
);
if (url) {
console.log("Download URL:", url);
console.log("Content type:", metadata?.contentType);
} else {
console.log("File not found");
}

Con un bucket específico:

const { url } = await client.storage.getSignedUrl(
"camera.jpg",
"product-images" // bucket
);

El SDK almacena en caché las URL firmadas para evitar llamadas redundantes al servidor, hasta que caduca el token de descarga de cada una. Las consultas hechas juntas — una página de vistas previas que llama a getSignedUrl() en el mismo tick — van al servidor como un POST /api/storage/metadata por cada 100 archivos, que el limitador de tasa cuenta como una décima parte de las solicitudes que reemplaza; un archivo que quien llama no puede leer hace fallar solo esa llamada. La caché pertenece a la credencial que la pidió: cuando cambia el token de acceso —un cierre de sesión, otro usuario que inicia sesión, una actualización—, empieza de cero, de modo que a un usuario nunca se le entrega una URL generada para otro.

  • Los archivos privados obtienen una URL con un token de descarga de corta duración y limitado a la ruta (?token=…, 5 minutos salvo que el servidor establezca STORAGE_DOWNLOAD_TOKEN_TTL, que 0.23 no lee) — nunca su token de acceso. Como caduca, no persista una URL privada; almacene la ruta del archivo y vuelva a llamar a getSignedUrl() al renderizarlo.
  • Los archivos públicos (almacenados bajo el prefijo public/ — establezca storage: { public: true } en la propiedad, o pase public: true a putObject) obtienen una URL estable, sin token, permanente y cacheable por CDN, sin ida y vuelta al servidor. Son seguros para almacenar en una base de datos y enlazar directamente.

Un texto que incrusta un archivo — las imágenes de un campo markdown — tampoco puede contener una URL privada, así que contiene una referencia de almacenamiento en su lugar: rebase-storage:posts/cover.png, con ?storageId=media cuando el archivo está en un origen de almacenamiento con nombre. Eso es lo que escribe el editor de markdown del panel de administración para una imagen subida. Intercambie las referencias por URL al renderizar el texto:

import { resolveStorageReferences } from "@rebasepro/client";
const post = await client.data.collection("posts").findById("post-1");
const body = await resolveStorageReferences(String(post?.body ?? ""), client);
// `body` is the same markdown, each reference replaced by a fresh URL.

Cada objeto se firma una vez por llamada, desde su propio origen, de modo que una imagen privada sigue funcionando sin importar cuánto tiempo haga que se subió. Una referencia que nombra un objeto que ya no existe se deja como está. storageReference(key, storageId?) construye una, para texto que usted mismo escriba.

El texto guardado antes de que existieran las referencias contiene la propia URL de descarga de un archivo privado, …/storage/file/posts/cover.png?token=…, cuyo token caducó pocos minutos después de escribirse. resolveStorageReferences también las renueva: el token sigue nombrando el objeto, así que la URL se sustituye por una nueva. Para guardar referencias en su lugar, reescribe el texto una vez con storageReferencesForDownloadUrls(text), que deja intacta cualquier otra URL.

Recupere un archivo como un objeto File:

const file = await client.storage.getObject("products/images/camera.jpg");
if (file) {
console.log("File name:", file.name);
console.log("File type:", file.type);
console.log("File size:", file.size);
// Create a download link
const url = URL.createObjectURL(file);
window.open(url);
} else {
console.log("File not found");
}

Con un bucket específico:

const file = await client.storage.getObject("camera.jpg", "product-images");
await client.storage.deleteObject("products/images/camera.jpg");
// With bucket
await client.storage.deleteObject("camera.jpg", "product-images");

Eliminar un archivo inexistente no lanza un error.

Liste archivos por prefijo, con paginación opcional:

const result = await client.storage.listObjects("products/images/", {
bucket: "uploads",
maxResults: 50,
pageToken: undefined // for pagination
});
for (const item of result.items) {
console.log(item.fullPath, item.name, item.size, item.updated, item.contentType);
}
// Paginate
if (result.nextPageToken) {
const nextPage = await client.storage.listObjects("products/images/", {
pageToken: result.nextPageToken
});
}

Cada elemento lleva su size en bytes y updated (una marca de tiempo ISO 8601) en almacenamiento local, S3 y GCS, y su contentType en almacenamiento local y GCS — lo suficiente para mostrar una carpeta sin un getSignedUrl() por archivo. Un listado de S3 no tiene tipo de contenido; las carpetas (result.prefixes) no llevan ninguno de los tres.

El SDK gestiona de forma transparente los prefijos de las claves de almacenamiento. Puede pasar claves con o sin el prefijo de protocolo:

// All equivalent — the SDK strips the prefix internally
await client.storage.getSignedUrl("local://products/image.jpg");
await client.storage.getSignedUrl("s3://products/image.jpg");
await client.storage.getSignedUrl("products/image.jpg");
Método Descripción Devuelve
putObject({ file, key?, bucket?, metadata? }) Subir un archivo UploadFileResult
getSignedUrl(key, bucket?) Obtener URL de descarga + metadatos DownloadConfig
getObject(key, bucket?) Descargar como objeto File File | null
deleteObject(key, bucket?) Eliminar un archivo void
listObjects(prefix, options?) Listar archivos por prefijo StorageListResult