Aller au contenu

Stockage et fichiers

Le module client.storage fournit des méthodes de gestion des fichiers — téléversement, téléchargement, listage et suppression. Il fonctionne aussi bien avec le disque local qu’avec des backends de stockage compatibles S3, selon la configuration de votre serveur.

Toutes les méthodes de stockage utilisent le transport partagé, de sorte que les tokens d’authentification sont injectés automatiquement.

Utilisez putObject() pour téléverser un fichier. Elle accepte un objet File ou Blob accompagné d’une clé de stockage et de métadonnées optionnelles :

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 fichier de plus de 8 Mio est envoyé par le point de terminaison reprenable (TUS) du serveur, à raison de 8 Mio par requête, au lieu d’une seule requête multipart que le serveur analyse en entier — un gros téléversement coûte donc au serveur un fragment de mémoire, et non trois à cinq fois le fichier. Un fragment qui échoue en route est renvoyé à partir de l’endroit où le serveur s’était arrêté. Le résultat, l’objet stocké et les vérifications du serveur sont les mêmes dans les deux cas. Lorsque le point de terminaison reprenable ne peut pas accepter le fichier (un serveur plus ancien, ou plusieurs instances qui ne partagent pas les téléversements), il part en un seul POST /api/storage/upload, et il en va de même pour chaque fichier suivant de ce client. Voir Téléversements reprenables (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);
}

Récupérez une URL de téléchargement et les métadonnées d’un fichier stocké :

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

Avec un bucket spécifique :

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

Le SDK met en cache les URL signées pour éviter les appels serveur redondants, jusqu’à l’expiration du jeton de téléchargement de chacune. Les recherches faites ensemble — une page d’aperçus qui appelle getSignedUrl() au même tick — partent vers le serveur sous la forme d’un seul POST /api/storage/metadata par tranche de 100 fichiers, que le limiteur de débit compte pour un dixième des requêtes qu’il remplace ; un fichier qu’un appelant n’a pas le droit de lire fait échouer cet appel seul. Le cache appartient à l’identifiant qui l’a demandé : lorsque le jeton d’accès change — une déconnexion, un autre utilisateur qui se connecte, une actualisation — il repart de zéro, de sorte qu’un utilisateur ne reçoit jamais une URL émise pour un autre.

  • Les fichiers privés obtiennent une URL avec un token de téléchargement à courte durée de vie, limité au chemin (?token=…, 5 minutes sauf si le serveur définit STORAGE_DOWNLOAD_TOKEN_TTL, que la 0.23 ne lit pas) — jamais votre token d’accès. Comme il expire, ne conservez pas d’URL privée ; stockez le chemin du fichier et rappelez getSignedUrl() au moment du rendu.
  • Les fichiers publics (stockés sous le préfixe public/ — définissez storage: { public: true } sur la propriété, ou passez public: true à putObject) obtiennent une URL stable, sans token, permanente et cacheable par CDN, sans aller-retour serveur. Ils peuvent être stockés en toute sécurité dans une base de données et liés directement.

Un texte qui intègre un fichier — les images d’un champ markdown — ne peut pas non plus contenir une URL privée, il contient donc à la place une référence de stockage : rebase-storage:posts/cover.png, avec ?storageId=media lorsque le fichier se trouve dans une source nommée. C’est ce que l’éditeur markdown du panneau d’administration écrit pour une image téléversée. Échangez les références contre des URL au moment de restituer le texte :

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.

Chaque objet est signé une fois par appel, depuis sa propre source, de sorte qu’une image privée continue de fonctionner quelle que soit l’ancienneté du téléversement. Une référence qui nomme un objet qui n’existe plus est laissée telle quelle. storageReference(key, storageId?) en construit une, pour un texte que vous écrivez vous-même.

Un texte enregistré avant l’existence des références contient lui-même l’URL de téléchargement d’un fichier privé, …/storage/file/posts/cover.png?token=…, dont le token a expiré quelques minutes après son écriture. resolveStorageReferences renouvelle aussi celles-ci : le token nomme toujours l’objet, l’URL est donc remplacée par une URL fraîche. Pour stocker des références à la place, réécrivez le texte une fois avec storageReferencesForDownloadUrls(text), qui laisse toutes les autres URL telles quelles.

Récupérez un fichier sous forme d’objet 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");
}

Avec un bucket spécifique :

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

Supprimer un fichier inexistant ne lève pas d’erreur.

Listez les fichiers par préfixe, avec pagination optionnelle :

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

Chaque élément porte sa size en octets et updated (un horodatage ISO 8601) sur le stockage local, S3 et GCS, et son contentType sur le stockage local et GCS — de quoi afficher un dossier sans un getSignedUrl() par fichier. Une liste S3 n’a pas de type de contenu ; les dossiers (result.prefixes) ne portent aucun des trois.

Le SDK gère de manière transparente les préfixes des clés de stockage. Vous pouvez passer des clés avec ou sans le préfixe de protocole :

// 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éthode Description Renvoie
putObject({ file, key?, bucket?, metadata? }) Téléverser un fichier UploadFileResult
getSignedUrl(key, bucket?) Obtenir l’URL de téléchargement + métadonnées DownloadConfig
getObject(key, bucket?) Télécharger sous forme d’objet File File | null
deleteObject(key, bucket?) Supprimer un fichier void
listObjects(prefix, options?) Lister les fichiers par préfixe StorageListResult