Zum Inhalt springen

Speicher & Dateien

Das Modul client.storage bietet Methoden zur Dateiverwaltung — Hochladen, Herunterladen, Auflisten und Löschen. Es funktioniert je nach Serverkonfiguration sowohl mit lokalem Speicher als auch mit S3-kompatiblen Speicher-Backends.

Alle Speichermethoden verwenden den gemeinsamen Transport, sodass Authentifizierungstokens automatisch eingefügt werden.

Verwenden Sie putObject(), um eine Datei hochzuladen. Es akzeptiert ein File- oder Blob-Objekt zusammen mit einem optionalen Speicherschlüssel und Metadaten:

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 }

Eine Datei über 8 MiB wird über den fortsetzbaren (TUS-) Endpunkt des Servers hochgeladen, 8 MiB pro Anfrage, statt als eine Multipart-Anfrage, die der Server vollständig parst — ein großer Upload kostet den Server so einen Chunk an Arbeitsspeicher, nicht das Drei- bis Fünffache der Datei. Ein Chunk, der unterwegs scheitert, wird ab dem Punkt erneut gesendet, bis zu dem der Server gekommen ist. Das Ergebnis, das gespeicherte Objekt und die Prüfungen des Servers sind in beiden Fällen dieselben. Kann der fortsetzbare Endpunkt die Datei nicht annehmen (ein älterer Server oder mehrere Instanzen, die Uploads nicht teilen), geht sie als ein POST /api/storage/upload, ebenso jede spätere Datei desselben Clients. Siehe Fortsetzbare Uploads (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);
}

Rufen Sie eine Download-URL und Metadaten für eine gespeicherte Datei ab:

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

Mit einem bestimmten Bucket:

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

Das SDK speichert signierte URLs im Cache, um redundante Serveraufrufe zu vermeiden, bis das Download-Token der jeweiligen URL abläuft. Gemeinsam gestellte Abfragen — eine Seite mit Vorschauen, die getSignedUrl() im selben Tick aufruft — gehen als ein POST /api/storage/metadata pro 100 Dateien an den Server, den der Rate Limiter als ein Zehntel der ersetzten Anfragen zählt; eine Datei, die ein Aufrufer nicht lesen darf, lässt nur diesen einen Aufruf fehlschlagen. Der Cache gehört zu den Zugangsdaten, die ihn befüllt haben: Ändert sich das Access-Token — bei einer Abmeldung, wenn sich ein anderer Benutzer anmeldet oder bei einer Aktualisierung —, beginnt er von vorn, sodass nie ein Benutzer eine URL erhält, die für einen anderen erzeugt wurde.

  • Private Dateien erhalten eine URL mit einem kurzlebigen, pfadbeschränkten Download-Token (?token=…, 5 Minuten, sofern der Server nicht STORAGE_DOWNLOAD_TOKEN_TTL setzt, was 0.23 nicht liest) — niemals Ihr Access-Token. Da es abläuft, speichern Sie keine private URL; speichern Sie den Pfad der Datei und rufen Sie getSignedUrl() beim Rendern erneut auf.
  • Öffentliche Dateien (unter dem Präfix public/ gespeichert — setzen Sie storage: { public: true } auf die Property oder übergeben Sie public: true an putObject) erhalten eine stabile, tokenlose, dauerhafte, CDN-cachefähige URL ohne Server-Roundtrip. Diese können sicher in einer Datenbank gespeichert und direkt verlinkt werden.

Text, der eine Datei einbettet — die Bilder in einem Markdown-Feld — kann ebenfalls keine private URL halten, sondern hält stattdessen eine Storage-Referenz: rebase-storage:posts/cover.png, mit ?storageId=media, wenn die Datei in einer benannten Quelle liegt. Das ist es, was der Markdown-Editor des Admin-Panels für ein hochgeladenes Bild schreibt. Tauschen Sie die Referenzen beim Rendern des Texts gegen URLs aus:

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.

Jedes Objekt wird einmal pro Aufruf signiert, aus seiner eigenen Quelle, sodass ein privates Bild funktionsfähig bleibt, egal wie lange es schon hochgeladen ist. Eine Referenz, die ein nicht mehr existierendes Objekt benennt, bleibt unverändert. storageReference(key, storageId?) erstellt eine solche Referenz, für Text, den Sie selbst schreiben.

Text, der gespeichert wurde, bevor es Referenzen gab, enthält die Download-URL einer privaten Datei selbst, …/storage/file/posts/cover.png?token=…, deren Token wenige Minuten nach dem Schreiben ablief. resolveStorageReferences erneuert auch diese: Das Token nennt weiterhin das Objekt, sodass die URL durch eine frische ersetzt wird. Um stattdessen Referenzen zu speichern, schreiben Sie den Text einmal mit storageReferencesForDownloadUrls(text) um, was jede andere URL unverändert lässt.

Rufen Sie eine Datei als File-Objekt ab:

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

Mit einem bestimmten Bucket:

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

Das Löschen einer nicht vorhandenen Datei wirft keinen Fehler.

Listen Sie Dateien nach Präfix auf, mit optionaler Paginierung:

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

Jeder Eintrag trägt seine size in Bytes und updated (einen ISO-8601- Zeitstempel) bei lokalem Speicher, S3 und GCS und seinen contentType bei lokalem Speicher und GCS — genug, um einen Ordner ohne ein getSignedUrl() pro Datei anzuzeigen. Eine S3- Auflistung hat keinen Content-Type; Ordner (result.prefixes) tragen keines der drei.

Das SDK verarbeitet Präfixe von Speicherschlüsseln transparent. Sie können Schlüssel mit oder ohne Protokollpräfix übergeben:

// 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");
Methode Beschreibung Rückgabe
putObject({ file, key?, bucket?, metadata? }) Eine Datei hochladen UploadFileResult
getSignedUrl(key, bucket?) Download-URL + Metadaten abrufen DownloadConfig
getObject(key, bucket?) Als File-Objekt herunterladen File | null
deleteObject(key, bucket?) Eine Datei löschen void
listObjects(prefix, options?) Dateien nach Präfix auflisten StorageListResult