Salta ai contenuti

Archiviazione e file

Il modulo client.storage fornisce metodi per la gestione dei file — caricamento, download, elenco ed eliminazione. Funziona sia con disco locale sia con backend di archiviazione compatibili con S3, a seconda della configurazione del server.

Tutti i metodi di archiviazione utilizzano il trasporto condiviso, quindi i token di autenticazione vengono iniettati automaticamente.

Usa putObject() per caricare un file. Accetta un oggetto File o Blob insieme a una chiave di archiviazione e metadati opzionali:

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 file oltre 8 MiB sale attraverso l’endpoint riprendibile (TUS) del server, 8 MiB per richiesta, invece che come un’unica richiesta multipart che il server analizza per intero — così un caricamento grande costa al server un chunk di memoria, non da tre a cinque volte il file. Un chunk che fallisce lungo la strada viene inviato di nuovo da dove il server era arrivato. Il risultato, l’oggetto memorizzato e i controlli del server sono gli stessi in entrambi i casi. Quando l’endpoint riprendibile non può accettare il file (un server più vecchio, o più istanze che non condividono i caricamenti), il file va come un unico POST /api/storage/upload, e lo stesso vale per ogni file successivo di quel client. Vedi Caricamenti riprendibili (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);
}

Recupera un URL di download e i metadati di un file archiviato:

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 specifico:

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

L’SDK memorizza nella cache gli URL firmati per evitare chiamate ridondanti al server, fino alla scadenza del token di download di ciascuno. Le ricerche fatte insieme — una pagina di anteprime che chiama getSignedUrl() nello stesso tick — vanno al server come un solo POST /api/storage/metadata ogni 100 file, che il rate limiter conta come un decimo delle richieste che sostituisce; un file che un chiamante non può leggere fa fallire solo quella chiamata. La cache appartiene alla credenziale che l’ha richiesta: quando il token di accesso cambia — una disconnessione, un altro utente che accede, un refresh — riparte da zero, così a un utente non viene mai consegnato un URL generato per un altro.

  • I file privati ottengono un URL con un token di download a breve durata, limitato al percorso (?token=…, 5 minuti salvo che il server imposti STORAGE_DOWNLOAD_TOKEN_TTL, che la 0.23 non legge) — mai il tuo token di accesso. Poiché scade, non conservare un URL privato; memorizza il percorso del file e richiama getSignedUrl() al momento del rendering.
  • I file pubblici (archiviati sotto il prefisso public/ — imposta storage: { public: true } sulla proprietà, o passa public: true a putObject) ottengono un URL stabile, senza token, permanente e memorizzabile nella CDN, senza andata e ritorno al server. Sono sicuri da memorizzare in un database e da collegare direttamente.

Un testo che incorpora un file — le immagini in un campo markdown — non può contenere nemmeno un URL privato, quindi contiene invece un riferimento di storage: rebase-storage:posts/cover.png, con ?storageId=media quando il file si trova in una sorgente nominata. È ciò che scrive l’editor markdown del pannello di amministrazione per un’immagine caricata. Scambia i riferimenti con URL quando esegui il rendering del testo:

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.

Ogni oggetto viene firmato una volta per chiamata, dalla propria sorgente, così un’immagine privata continua a funzionare indipendentemente da quanto tempo fa è stata caricata. Un riferimento che nomina un oggetto che non esiste più viene lasciato com’è. storageReference(key, storageId?) ne costruisce uno, per un testo che scrivi tu stesso.

Un testo salvato prima che i riferimenti esistessero contiene l’URL di download di un file privato, …/storage/file/posts/cover.png?token=…, il cui token è scaduto pochi minuti dopo la scrittura. resolveStorageReferences rinnova anche questi: il token nomina ancora l’oggetto, quindi l’URL viene sostituito da uno nuovo. Per memorizzare invece dei riferimenti, riscrivi il testo una volta con storageReferencesForDownloadUrls(text), che lascia ogni altro URL com’è.

Recupera un file come oggetto 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 specifico:

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");

L’eliminazione di un file inesistente non genera un errore.

Elenca i file per prefisso, con paginazione opzionale:

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
});
}

Ogni elemento porta la sua size in byte e updated (un timestamp ISO 8601) su storage locale, S3 e GCS, e il suo contentType su storage locale e GCS — abbastanza per mostrare una cartella senza un getSignedUrl() per file. Un elenco S3 non ha il content type; le cartelle (result.prefixes) non portano nessuno dei tre.

L’SDK gestisce in modo trasparente i prefissi delle chiavi di archiviazione. Puoi passare le chiavi con o senza il prefisso di protocollo:

// 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");
Metodo Descrizione Restituisce
putObject({ file, key?, bucket?, metadata? }) Caricare un file UploadFileResult
getSignedUrl(key, bucket?) Ottenere l’URL di download + metadati DownloadConfig
getObject(key, bucket?) Scaricare come oggetto File File | null
deleteObject(key, bucket?) Eliminare un file void
listObjects(prefix, options?) Elencare i file per prefisso StorageListResult