Wählen Sie einen Speicheradapter für hochgeladene Medien. Eine Datenbanksicherung enthält Medienmetadaten, nicht die gespeicherten Dateien, sichern Sie also das Speicher-Backend separat.
Übersicht
| Speicher | Verwenden wenn | Signierte Uploads |
|---|---|---|
| R2 Binding | Die Website läuft auf Cloudflare Workers | Nein |
| S3 | Eine Node.js-Website nutzt AWS S3, R2’s S3 API, MinIO oder kompatiblen Speicher | Ja |
| Lokal | Eine Node.js-Website hat ein beschreibbares persistentes Volume | Nein |
Cloudflare R2 Binding
Verwenden Sie den R2-Binding-Adapter auf Cloudflare Workers. Das Binding stellt Zugriff zur Laufzeit bereit, sodass die Website keine R2-Zugriffsschlüssel benötigt.
import emdash from "emdash/astro";
import { r2 } from "@emdash-cms/cloudflare";
export default defineConfig({
integrations: [
emdash({
storage: r2({ binding: "MEDIA" }),
}),
],
});
Konfiguration
| Option | Typ | Beschreibung |
|---|---|---|
binding | string | R2-Binding-Name aus wrangler.jsonc |
publicUrl | string | Optionale öffentliche URL für den Bucket |
Einrichtung
Fügen Sie das R2-Binding zu Ihrer Wrangler-Konfiguration hinzu:
wrangler.jsonc
{
"r2_buckets": [
{
"binding": "MEDIA",
"bucket_name": "emdash-media"
}
]
} wrangler.toml
[[r2_buckets]]
binding = "MEDIA"
bucket_name = "emdash-media" Öffentlicher Zugriff
Um Medien von einem öffentlichen Bucket bereitzustellen, verbinden Sie eine benutzerdefinierte Domain mit der Cloudflare API und setzen Sie deren Origin als publicUrl. Cloudflares r2.dev-Entwicklungs-URL ist ratenbegrenzt und nicht für Produktionstraffic gedacht.
storage: r2({
binding: "MEDIA",
publicUrl: "https://media.example.com",
});
Wenn derselbe Bucket automatische JSON-Backups speichert, kann ein öffentlicher Bucket-Origin Objekte unter backups/ offenlegen. Verwenden Sie einen privaten Bucket und EmDashs Medienroute oder beschränken Sie den öffentlichen Origin auf Medienobjekte. Siehe Backups.
S3-kompatibler Speicher
Der S3-Adapter funktioniert unter Node.js mit Cloudflare R2’s S3 API, AWS S3, MinIO und kompatiblen Diensten.
Die folgende Konfiguration löst Endpoint, Bucket, Credentials, Region und optionale öffentliche URL aus S3_*-Variablen auf, wenn der Node.js-Prozess startet:
import emdash, { s3 } from "emdash/astro";
export default defineConfig({
integrations: [
emdash({
storage: s3(),
}),
],
});
Konfiguration
| Option | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
endpoint | string | ja | S3-Endpoint-URL |
bucket | string | ja | Bucket-Name |
accessKeyId | string | nein* | Zugriffsschlüssel |
secretAccessKey | string | nein* | Geheimer Schlüssel |
region | string | nein | Region (Standard: "auto") |
publicUrl | string | nein | Optionale CDN- oder öffentliche URL |
* Sowohl accessKeyId als auch secretAccessKey müssen zusammen angegeben oder beide weggelassen werden.
S3-Konfiguration aus Umgebungsvariablen auflösen
Jedes in s3({...}) ausgelassene Feld wird beim Prozessstart aus der entsprechenden S3_*-Umgebungsvariable gelesen. So können Sie ein Container-Image einmal bauen und Credentials beim Start injizieren, ohne neu zu bauen. Explizite Werte in s3({...}) haben immer Vorrang vor Umgebungsvariablen.
| Umgebungsvariable | Feld | Hinweise |
|---|---|---|
S3_ENDPOINT | endpoint | Muss eine gültige http/https-URL sein |
S3_BUCKET | bucket | |
S3_ACCESS_KEY_ID | accessKeyId | |
S3_SECRET_ACCESS_KEY | secretAccessKey | |
S3_REGION | region | Standard: "auto" |
S3_PUBLIC_URL | publicUrl | Optionales CDN-Präfix |
Umgebungsvariablen werden beim Prozessstart aus process.env gelesen. Dies ist eine reine Node-Funktion.
Der Aufruf von s3() ohne Argumente liest jedes Feld aus den S3_*-Umgebungsvariablen:
import emdash, { s3 } from "emdash/astro";
export default defineConfig({
integrations: [
emdash({
// s3() ohne Argumente: alle Felder aus S3_*-Umgebungsvariablen
storage: s3(),
// Oder mischen: ein Feld überschreiben, Rest aus der Umgebung
// storage: s3({ publicUrl: "https://cdn.example.com" }),
}),
],
});
R2 über S3 API
Verwenden Sie den S3-Adapter unter Node.js, wenn direkte signierte Uploads zu R2 erforderlich sind. Erstellen Sie begrenzte R2 API-Credentials mit der Cloudflare API oder CLI und setzen Sie dann die folgenden Laufzeitvariablen:
S3_ENDPOINT=https://<account-id>.r2.cloudflarestorage.com
S3_BUCKET=emdash-media
S3_ACCESS_KEY_ID=<r2-access-key-id>
S3_SECRET_ACCESS_KEY=<r2-secret-access-key>
S3_REGION=auto
S3_PUBLIC_URL=https://media.example.com
Bewahren Sie die echten Werte im Secret Manager der Node.js-Hosting-Plattform auf. Die öffentliche URL ist optional und ersetzt nicht den S3 API-Endpoint, der für Uploads verwendet wird.
MinIO
Richten Sie die gleichen Laufzeitvariablen auf MinIO. Setzen Sie S3_ENDPOINT auf den MinIO API-Origin, S3_BUCKET auf den Bucket-Namen und die beiden Credential-Variablen auf einen begrenzten MinIO-Zugriffsschlüssel. Setzen Sie S3_PUBLIC_URL nur, wenn dieser Origin die Objekte des Buckets öffentlich bereitstellt.
Lokales Dateisystem
Verwenden Sie lokalen Speicher für die Entwicklung oder einen einzelnen Node.js-Server mit persistenter Festplatte. Dateien werden in einem Verzeichnis auf dieser Festplatte gespeichert.
import emdash, { local } from "emdash/astro";
export default defineConfig({
integrations: [
emdash({
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
}),
],
});
Konfiguration
| Option | Typ | Beschreibung |
|---|---|---|
directory | string | Verzeichnispfad für Dateispeicher |
baseUrl | string | Basis-URL zum Bereitstellen von Dateien |
Die baseUrl sollte mit EmDashs Medien-Datei-Endpoint (/_emdash/api/media/file) übereinstimmen, es sei denn, Sie konfigurieren einen benutzerdefinierten statischen Dateiserver.
Separaten Speicher für verschiedene Umgebungen verwenden
Die folgende Konfiguration verwendet während der Entwicklung ein lokales Verzeichnis und R2 im Cloudflare-Produktions-Build:
import emdash, { local } from "emdash/astro";
import { r2 } from "@emdash-cms/cloudflare";
const storage = import.meta.env.PROD
? r2({ binding: "MEDIA" })
: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
});
export default defineConfig({
integrations: [emdash({ storage })],
});
Signierte Uploads
Der S3-Adapter unterstützt signierte Upload-URLs, die es Clients ermöglichen, direkt in den Speicher hochzuladen, ohne über Ihren Server zu gehen. Dies verbessert die Leistung bei großen Dateien.
Signierte Uploads sind automatisch, wenn der S3-Adapter verwendet wird. Die Admin-Oberfläche nutzt sie, wenn verfügbar.
Adapter, die signierte Uploads unterstützen:
- S3 (einschließlich R2 über S3 API)
Adapter, die keine signierten Uploads unterstützen:
- R2 Binding (verwenden Sie stattdessen den S3-Adapter mit R2-Credentials)
- Lokal
Speicher-Interface
Alle Speicheradapter implementieren das gleiche Interface:
interface Storage {
upload(options: {
key: string;
body: Buffer | Uint8Array | ReadableStream;
contentType: string;
}): Promise<UploadResult>;
download(key: string, options?: DownloadOptions): Promise<DownloadResult>;
delete(key: string): Promise<void>;
exists(key: string): Promise<boolean>;
list(options?: ListOptions): Promise<ListResult>;
getSignedUploadUrl(options: SignedUploadOptions): Promise<SignedUploadUrl>;
getPublicUrl(key: string): string;
}
Diese Konsistenz ermöglicht den Wechsel von Speicher-Backends, ohne den Anwendungscode zu ändern.
Byte-Bereiche
Die Medienroute /_emdash/api/media/file/ beantwortet HTTP-Range-Anfragen. Safari spielt Videos nur von Servern ab, die sie unterstützen, und andere Browser nutzen sie zum Vor- und Zurückspulen. Die Route übergibt den angeforderten Bereich als options.range an download(); das ist entweder { offset, length } oder { suffix } für die letzten suffix Bytes. length entfällt bei einem offenen Bereich wie bytes=500-.
Ein Adapter, der Bereiche unterstützt, liefert den Teil des angeforderten Bereichs, der innerhalb der Datei liegt, und setzt im Ergebnis range: { offset, length }, um ihn zu beschreiben. size ist immer die Größe der gesamten Datei. Kann ein Adapter einen Bereich nicht bedienen, auch wenn dieser hinter dem Dateiende beginnt, liefert er die ganze Datei ohne range zurück. Die Route antwortet dann mit der ganzen Datei oder, wenn der Bereich hinter dem Ende beginnt, mit 416 Range Not Satisfiable.
Die Adapter für R2, S3 und das lokale Dateisystem unterstützen Bereiche. Für einen benutzerdefinierten Adapter ist die Bereichsunterstützung optional: Ignoriert er options, erhalten Bereichsanfragen die ganze Datei oder 416, wenn der Bereich hinter dem Ende beginnt.