Stockage
Chaque projet doté d'une base a un stockage de fichiers, organisé en seaux, servi à l'adresse du projet au format de storage-js. Cette page dit comment s'en servir, comment l'accès aux fichiers se décide, ce que chaque palier permet, et ce qui n'est pas encore servi, à commencer par le point d'accès compatible S3.
Ce qui est servi
Le stockage est servi sous https://<identifiant>.donnees.bastide.app/storage/v1 (voir Base de données pour l'adresse). Votre application garde supabase-js : les chemins, les corps et les réponses sont ceux que storage-js appelle.
Geste de storage-js | Servi |
|---|---|
createBucket, getBucket, listBuckets, updateBucket, deleteBucket, emptyBucket | oui |
upload, update, download, list, remove, move, copy, info, exists | oui |
createSignedUrl, createSignedUrls, createSignedUploadUrl, uploadToSignedUrl | oui |
getPublicUrl | oui |
| Téléversement repris (tus) | oui |
Transformation d'images (/storage/v1/render/image/…) | oui, si l'installation porte le service de rendu ; sinon 501 |
Point compatible S3 (/storage/v1/s3) | non : 404, code chemin_inconnu (LIM-07) |
Note. Le point compatible S3 (pour le SDK AWS,
rcloneou tout client S3) n'est pas encore servi. Il est prévu avec le lot L2 ; le servir à moitié ferait écrire une application contre une surface qui changera. Employezstorage-js, ou l'API HTTP qu'il appelle.
Il n'y a pas d'écran de la console pour les seaux et les fichiers, ni d'outil MCP : le stockage se pilote depuis votre application. L'écran Données affiche le point d'entrée /storage/v1 dans la carte « Base PostgreSQL ».
Les seaux
Un seau se crée avec trois réglages :
- public : un seau public sert ses fichiers sans clé, par l'URL publique ;
- taille maximale d'un fichier : la plus petite de celle du seau et de celle du palier s'applique ;
- types admis : une liste de types MIME,
image/*admet toute image ; la comparaison ignore la casse et les paramètres. Une liste vide admet tout.
import { createClient } from "@supabase/supabase-js";
const supabase = createClient("https://<identifiant>.donnees.bastide.app", "<clé publiable>");
await supabase.storage.createBucket("avatars", {
public: false,
fileSizeLimit: "5MB",
allowedMimeTypes: ["image/*"],
});
await supabase.storage.from("avatars").upload(`${userId}/portrait.png`, fichier);
Créer un seau demande en pratique la clé secrète, côté serveur, ou une politique qui l'autorise.
Qui peut lire et écrire
Chaque requête s'exécute sous le rôle de l'appelant : le rôle anonyme avec la seule clé publiable, le rôle authentifié avec un jeton de session, le rôle de service avec une clé secrète. Ce sont les politiques de la table storage.objects de votre base qui décident ; la plateforme ne porte aucune règle d'accès aux fichiers. Une lecture refusée l'est par la politique, pas par l'application.
Une politique de stockage s'écrit comme une autre (voir Sécurité des données). Par exemple, chacun ne lit que son dossier :
create policy "lire son dossier" on storage.objects for select
using (bucket_id = 'avatars' and (storage.foldername(name))[1] = (select auth.uid())::text);
Trois chemins se passent de clé, parce qu'un navigateur n'ajoute aucun en-tête à une balise <img> ou à un formulaire : l'URL publique d'un seau public, l'URL signée de lecture, et l'URL signée de téléversement. La permission d'une URL signée est jugée quand elle est signée.
- Une URL signée vit une heure par défaut, sept jours au plus ; au-delà, 400
duree_refusee. - Un téléversement est d'abord éprouvé sous votre politique : un refus coûte une requête, pas un fichier envoyé.
Les limites par palier
| Palier | Taille maximale d'un fichier | Stockage inclus |
|---|---|---|
| Découverte | 50 Mo | 5 Go |
| Standard | 5 Go | 50 Go |
| Pro | 50 Go | 250 Go |
| Souverain | aucune borne de palier | 1 To |
Les tailles se comptent en puissances de 1 024. Aujourd'hui, rien n'est facturé au-delà de l'inclus : l'inclus est le quota.
- À 80 % du quota, le franchissement est consigné au journal d'audit du projet.
- À 100 %, toute nouvelle écriture est refusée (413,
quota_atteint) avec un message qui nomme le quota ; les lectures continuent.
Le nettoyage
Chaque jour, une passe par projet retire ce qui ne sert plus : un téléversement repris que personne n'a nourri depuis 24 heures, un téléversement en plusieurs parties abandonné depuis 24 heures, et des octets qu'aucun objet ne désigne depuis une heure. Chaque passe qui retire quelque chose se consigne.
Sauvegarde des fichiers
Les fichiers de vos seaux ne sont pas restaurés avec la base : une restauration de base rend les lignes de storage.objects à l'instant du dump, pas les octets. Gardez ailleurs une copie de ce qui ne se reconstruit pas (voir Sauvegardes).
Erreurs fréquentes
| Code | Statut | Cause et remède |
|---|---|---|
refuse_par_la_politique | 403 | Une politique de storage.objects refuse ce geste à cet appelant. |
fichier_trop_grand | 413 | Le fichier dépasse la borne du seau ou du palier ; le message dit laquelle. |
quota_atteint | 413 | Le stockage du projet est plein ; supprimez des fichiers ou changez de palier. |
type_refuse | 415 | Le seau n'admet pas ce type ; le message liste les types admis. |
seau_introuvable | 404 | Le seau n'existe pas, ou la politique ne vous le laisse pas voir. |
existe_deja | 409 | Un objet porte déjà ce nom ; téléversez avec upsert: true pour le remplacer. |
nom_refuse | 400 | Le nom de l'objet n'est pas admis. |
duree_refusee | 400 | L'URL signée demandée dépasse sept jours. |
chemin_inconnu | 404 | Le chemin n'est pas servi, par exemple le point S3. |