Bedingte Dokument-Downloads hinzufügen

CloudflareBeginner
Jetzt üben

Einführung

Eine Dokumentanzeige benötigt oft nur die nächsten wenigen Bytes oder eine Bestätigung, dass ihre zwischengespeicherte Kopie noch aktuell ist. Die gesamte Datei bei jeder Anfrage herunterzuladen, verursacht unnötigen Aufwand. Sie fügen einem geschützten Worker, der auf privatem R2-Speicher basiert, HTTP-Validatoren und Downloads einzelner Bytebereiche hinzu.

Bearbeiten Sie zuerst Stream Documents Through a Worker. Dieses Lab startet in einer neuen VM mit Node.js 22.22.0, Wrangler 4.131.1 und einem bereitgestellten Modul zur Tokenprüfung. Sie erstellen einen neuen Bucket und stellen einen neuen Worker bereit. Ihr R2-Abonnement und die Berechtigungen Ihres Lernkontos müssen bereits eingerichtet sein. Informieren Sie sich unter R2 pricing über Vorgänge und Speicher. Eine benutzerdefinierte Domain ist nicht erforderlich. Es werden nur synthetische Textdaten gespeichert. Bereinigen Sie die Ressourcen, bevor Sie das Lab verlassen.

Anwendungs-Bucket verbinden

In diesem Schritt autorisieren Sie diese VM und erstellen einen unabhängigen privaten Bucket für die Anwendung. Die Geräteautorisierung bestätigt Ihr Lernkonto. Für die Verwaltung von R2-Buckets wird ein separates API-Token verwendet, das auf dieses Konto beschränkt ist.

Starten Sie Bash für die unten verwendete Befehlssyntax. Wechseln Sie anschließend in das vorbereitete Projekt und prüfen Sie die Tools. Lassen Sie dasselbe Terminal geöffnet, damit die Variablen mit den Ressourcennamen erhalten bleiben:

bash
cd /home/labex/project/r2-lab
export PATH="$PWD/.tools/node-v22.22.0-linux-x64/bin:$PATH"
node --version
npx wrangler --version

Autorisieren Sie den angezeigten Gerätecode in Ihrem eigenen Browser. Prüfen Sie vor der Zustimmung das Lernkonto sowie die angeforderten Lese-Bereiche für Konto und Benutzer:

npx wrangler login --device --browser=false --scopes account:read user:read workers_scripts:write workers_kv:write
npx wrangler whoami --json

Verlangen Sie loggedIn: true. Lesen Sie den Kontonamen aus, auch wenn nur ein Konto aufgelistet ist. Ersetzen Sie YOUR_ACCOUNT_ID unten durch die tatsächliche 32-stellige ID dieses Kontos. openssl rand -hex 6 erzeugt zwölf zufällige hexadezimale Zeichen, damit dieses Lab nicht mit einem früheren Durchlauf kollidiert. Das Here-Dokument schreibt eine standardmäßige Konfigurationsdatei. Die Shell setzt dabei Ihre Variablen ein.

ACCOUNT_ID=YOUR_ACCOUNT_ID
RUN_ID=$(openssl rand -hex 6)
NAME="labex-c05-r03-$RUN_ID"
BUCKET="$NAME-docs"
cat > wrangler.jsonc <<JSON
{"name":"$NAME","account_id":"$ACCOUNT_ID","main":"src/index.js","workers_dev":true,"compatibility_date":"2026-07-30","r2_buckets":[{"binding":"DOCUMENTS","bucket_name":"$BUCKET"}]}
JSON

Öffnen Sie für die Bucket-Verwaltung in Ihrem Cloudflare-Profil die Seite API Tokens und erstellen Sie ein benutzerdefiniertes Token mit einem Namen, der auf dieses Lab verweist. Gewähren Sie Account → Workers R2 Storage → Edit und beschränken Sie Account Resources auf das Lernkonto, dessen ID Sie gespeichert haben. Legen Sie einen kurzen Ablaufzeitraum fest. Schließen Sie keine anderen Konten oder nicht benötigten Berechtigungen ein. Dieses Verwaltungstoken dient der Bucket-Verwaltung, einschließlich Erstellung und Löschung. In diesem Lab greift der Worker über sein DOCUMENTS-Binding auf R2-Objekte zu.

Kopieren Sie das Token einmal in die versteckte Eingabeaufforderung dieser VM. umask 077 beschränkt den Dateizugriff auf Ihren Benutzer. read -s blendet die Eingabe aus. Die Datei verwendet Wranglers standardmäßige Token-Variable und wird von Git ausgeschlossen.

umask 077
read -r -s -p 'R2 management API token: ' R2_MANAGEMENT_TOKEN; printf '\n'
printf 'CLOUDFLARE_API_TOKEN=%s\n' "$R2_MANAGEMENT_TOKEN" > .env.management
unset R2_MANAGEMENT_TOKEN

Verwenden Sie --env-file=.env.management nur für Verwaltungsbefehle von R2. Der normale Befehl whoami prüft weiterhin die Geräteautorisierung der VM.

Setzen Sie --env-file an das Ende jedes Wrangler-Befehls, damit dessen Liste von Dateiargumenten nicht auch den Befehlsnamen erfasst. Wenn Wrangler nach dem Erstellen eines Buckets anbietet, eine Bindung zur Konfiguration hinzuzufügen, geben Sie n ein und drücken Sie die Eingabetaste. Die vorgesehene Bindung ist bereits konfiguriert.

npx wrangler r2 bucket create "$BUCKET" --env-file=.env.management

Listen Sie Ihre Buckets auf und suchen Sie den exakt generierten Namen. Andere Buckets gehören zu anderen Arbeiten. Lassen Sie diese unverändert.

npx wrangler r2 bucket list --env-file=.env.management

Öffnen Sie im Dashboard Storage & databases → R2 → Overview, wählen Sie genau diesen Bucket aus und prüfen Sie die leere Objektliste. Lassen Sie in den Einstellungen die öffentliche Entwicklungs-URL und benutzerdefinierte Domains deaktiviert. Ein Bucket-Name im Dashboard bestätigt die Identität. Die späteren Download-Prüfungen bestätigen die gespeicherten Bytes.

Die Berechtigung für Worker-Skripte ermöglicht die Bereitstellung. Die KV-Berechtigung unterstützt Wranglers Löschverwaltung. Dieses Lab erstellt keinen KV-Namespace. Das R2-Verwaltungstoken bleibt ein separates, auf das Konto beschränktes Zugangsmittel.

Bedingte und partielle Lesevorgänge implementieren

In diesem Schritt verwenden Sie R2-Metadaten, um zu entscheiden, ob ein Antworttext benötigt wird. Ein ETag fungiert wie eine Versionskennung für eine Datei. Wenn ein Client bereits eine Kopie besitzt, sendet er die Kennung in If-None-Match, um zu prüfen, ob sich die Datei geändert hat. Bei einer Übereinstimmung wird 304 Not Modified ohne Antworttext zurückgegeben. Dadurch müssen dieselben Bytes nicht erneut heruntergeladen werden. Mit einer Range-Anfrage kann eine Anzeige einen Teil einer großen Datei abrufen oder einen unterbrochenen Download fortsetzen. Die Anfrage verwendet inklusive Bytepositionen und erzeugt 206 Partial Content einschließlich eines Content-Range-Headers, der den Ausschnitt beschreibt.

Verwenden Sie diesen Handler. head() liest Metadaten, ohne Bytes abzurufen. Der spätere Aufruf von get() enthält onlyIf.etagMatches, damit ein Objekt, das zwischen den beiden Aufrufen geändert wurde, nicht anhand veralteter Metadaten zurückgegeben wird. Dieser Endpunkt unterstützt genau einen Bereich und ETag-basiertes If-Range. Nicht unterstützte Syntax für mehrere Bereiche gibt 400 zurück. Wenn sich ein If-Range-ETag unterscheidet, ermöglicht eine vollständige 200-Antwort dem Client, seine alte Kopie zu ersetzen.

cat > src/index.js <<'JS'
import { authorized } from "./auth.js";
export default {
  async fetch(request, env) {
    const path = new URL(request.url).pathname;
    if (path === "/health") return new Response("ok");
    if (!await authorized(request, env)) return new Response("Unauthorized", { status: 401 });
    if (request.method !== "GET") return new Response("Method not allowed", { status: 405 });
    if (path !== "/documents/report.txt") return new Response("Not found", { status: 404 });
    const key = path.slice(1);
    const metadata = await env.DOCUMENTS.head(key);
    if (!metadata) return new Response("Not found", { status: 404 });
    const headers = new Headers({ "ETag": metadata.httpEtag,
      "Last-Modified": metadata.uploaded.toUTCString(), "Accept-Ranges": "bytes",
      "Cache-Control": "private, no-store" });
    metadata.writeHttpMetadata(headers);
    // GET validators use weak comparison: W/"value" and "value" can match.
    const noneMatch = request.headers.get("If-None-Match");
    if (noneMatch && noneMatch.split(",").some(tag => tag.trim() === "*" || tag.trim().replace(/^W\//, "") === metadata.httpEtag))
      return new Response(null, { status: 304, headers });
    const since = Date.parse(request.headers.get("If-Modified-Since") || "");
    const uploadedSeconds = Math.floor(metadata.uploaded.getTime() / 1000) * 1000;
    if (!noneMatch && Number.isFinite(since) && uploadedSeconds <= since)
      return new Response(null, { status: 304, headers });
    let range = request.headers.get("Range");
    const ifRange = request.headers.get("If-Range");
    if (ifRange && ifRange !== metadata.httpEtag) range = null;
    let start = 0, end = metadata.size - 1;
    if (range) {
      const match = /^bytes=(\d*)-(\d*)$/.exec(range);
      // This endpoint supports exactly one range, not multipart ranges.
      if (!match || (!match[1] && !match[2]))
        return new Response("Invalid range", { status: 400 });
      if (!match[1]) { start = Math.max(0, metadata.size - Number(match[2])); }
      else { start = Number(match[1]); if (match[2]) end = Math.min(Number(match[2]), end); }
      if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start > end || start >= metadata.size) {
        headers.set("Content-Range", `bytes */${metadata.size}`);
        return new Response("Range not satisfiable", { status: 416, headers });
      }
      headers.set("Content-Range", `bytes ${start}-${end}/${metadata.size}`);
    }
    // Do not mix a HEAD result with bytes from an object replaced in between.
    const object = await env.DOCUMENTS.get(key, { onlyIf: { etagMatches: metadata.etag },
      ...(range ? { range: { offset: start, length: end - start + 1 } } : {}) });
    if (!object) return new Response("Not found", { status: 404 });
    if (!("body" in object)) return new Response("Object changed; retry", { status: 412 });
    headers.set("Content-Length", String(range ? end - start + 1 : metadata.size));
    return new Response(object.body, { status: range ? 206 : 200, headers });
  }
};
JS

Die angeforderte Startposition beginnt bei null. Ein Suffix wie bytes=-3 bezeichnet die letzten drei Bytes. Eine Startposition hinter dem Objekt erzeugt 416 mit Content-Range: bytes */SIZE. Die bedingte Validierung hat Vorrang vor der Auswahl des Bereichs. Wenn beide Header vorhanden sind, hat If-None-Match Vorrang vor der Datumsvalidierung.

Erstellen Sie das lokale Anwendungsgeheimnis und prüfen Sie das Bundle:

umask 077
printf "ACCESS_TOKEN=%s\n" "$(openssl rand -hex 24)" > .dev.vars
npx wrangler deploy --dry-run

Vollständige und bereichsweise lokale Antworttexte vergleichen

In diesem Schritt legen Sie ausschließlich lokalen Speicher an und prüfen echte HTTP-Header. Ein lokales Objekt ist vom späteren Remote-Objekt getrennt, obwohl beide denselben Schlüssel verwenden.

npx wrangler r2 object put "$BUCKET/documents/report.txt" --local --file document.txt --content-type text/plain
npx wrangler dev --ip 127.0.0.1 --port 8787 > dev.log 2>&1 &
DEV_PID=$!

Warten Sie auf die Bereitschaftsmeldung in dev.log und laden Sie anschließend das synthetische Anwendungsgeheimnis:

cat dev.log
set -a
source .dev.vars
set +a

Speichern Sie Header und Antworttext der vollständigen Antwort getrennt. -D schreibt die Header in eine Datei:

curl -fsS -D full.headers -H "Authorization: Bearer $ACCESS_TOKEN" http://127.0.0.1:8787/documents/report.txt -o full.txt
cmp document.txt full.txt
cat full.headers

Verlangen Sie 200, den gespeicherten Inhaltstyp, ein in Anführungszeichen gesetztes ETag und Accept-Ranges: bytes. Kopieren Sie den exakten ETag einschließlich der doppelten Anführungszeichen in ETAG innerhalb der unten gezeigten einfachen Anführungszeichen:

ETAG='"COPY_ETAG_HERE"'
curl -i -H "Authorization: Bearer $ACCESS_TOKEN" -H "If-None-Match: $ETAG" http://127.0.0.1:8787/documents/report.txt

Verlangen Sie 304 ohne Antworttext. Ein aktueller Validator vermeidet die vollständige Übertragung. Er macht den Bucket nicht öffentlich.

curl -sS -D range.headers -H "Authorization: Bearer $ACCESS_TOKEN" -H "Range: bytes=0-4" http://127.0.0.1:8787/documents/report.txt -o range.txt
head -c 5 document.txt > expected-range.txt
cmp expected-range.txt range.txt
cat range.headers

Verlangen Sie 206, Content-Range: bytes 0-4/SIZE und genau fünf übereinstimmende Bytes. Fordern Sie nun eine nicht erfüllbare Startposition an:

curl -i -H "Authorization: Bearer $ACCESS_TOKEN" -H "Range: bytes=99999-" http://127.0.0.1:8787/documents/report.txt

Verlangen Sie 416, den Header bytes */SIZE und Range not satisfiable. Die Plattformprüfung wiederholt diese Lesevorgänge unabhängig.

Bedingte Remote-Bereitstellung prüfen

In diesem Schritt stellen Sie die Remote-Testdaten unabhängig bereit und veröffentlichen den Handler. Beenden Sie zuerst den lokalen Server. Laden Sie anschließend dieselbe synthetische Datei mit dem expliziten Flag --remote hoch:

kill "$DEV_PID"
wait "$DEV_PID" 2>/dev/null || true
npx wrangler r2 object put "$BUCKET/documents/report.txt" --remote --file document.txt --content-type text/plain --env-file=.env.management
npx wrangler deploy
npx wrangler secret bulk .dev.vars

Kopieren Sie die bereitgestellte URL in BASE_URL. Warten Sie, bis der Health-Endpunkt ok zurückgibt. Wenn die neue Bereitstellung noch verteilt wird, wiederholen Sie die Lesevorgänge bis zu einer Minute lang.

BASE_URL=https://YOUR_WORKER.YOUR_SUBDOMAIN.workers.dev
curl -i "$BASE_URL/health"
curl -fsS -D remote.headers -H "Authorization: Bearer $ACCESS_TOKEN" "$BASE_URL/documents/report.txt" -o remote.txt
cmp document.txt remote.txt
cat remote.headers

Verwenden Sie den Remote-ETag aus remote.headers, nicht einen gemerkten lokalen Wert. Wiederholen Sie die bedingten und partiellen Anfragen:

ETAG='"COPY_REMOTE_ETAG_HERE"'
curl -i -H "Authorization: Bearer $ACCESS_TOKEN" -H "If-None-Match: $ETAG" "$BASE_URL/documents/report.txt"
curl -i -H "Authorization: Bearer $ACCESS_TOKEN" -H "Range: bytes=0-4" "$BASE_URL/documents/report.txt"
curl -i -H "Authorization: Bearer $ACCESS_TOKEN" -H "Range: bytes=99999-" "$BASE_URL/documents/report.txt"

Verlangen Sie 304 ohne Antworttext, 206 mit den ersten fünf Bytes der Testdatei und 416 mit der korrekten Größenbegrenzung. Prüfen Sie im Dashboard das genaue Worker-Binding und das Bucket-Objekt. Lassen Sie die öffentliche URL des Buckets und benutzerdefinierte Domains deaktiviert. HTTP-Header und der Vergleich der Antworttexte sind der maßgebliche Nachweis für die Bereichsanfragen.

DOCUMENTS-Bindung des Workers zum privaten R2-Bucket

Dieses Beispiel zeigt DOCUMENTS mit dem genauen privaten Bucket verbunden. Das Suffix deines generierten Namens wird abweichen.

Synthetischer Bericht in einem privaten Standard-Bucket

Die Objektzeile zeigt report.txt als text/plain, Standard und 41 B, während Public Access auf Disabled bleibt. Generierte Namen und Datumsangaben sind Beispiele. Der Gesamtwert Bucket Size kann verzögert noch 0 B anzeigen; die Objektzeile und der Bytevergleich belegen, dass die Datei existiert. HTTP-Header und Vergleiche des Antwortinhalts prüfen bedingte Antworten und Bereichsantworten.

Remote-Anwendung und Bucket entfernen

In diesem Schritt löschen Sie nur den Worker und die Objekte dieses Labs, solange Sie noch autorisiert sind. Der private Bucket verschwindet nicht, wenn der Worker gelöscht wird.

npx wrangler delete

Bestätigen Sie den exakt generierten Worker-Namen. Löschen Sie das eine hochgeladene Objekt ausdrücklich und anschließend den Bucket:

BUCKET=$(node -p "JSON.parse(require('fs').readFileSync('wrangler.jsonc')).r2_buckets[0].bucket_name")
npx wrangler r2 object delete "$BUCKET/documents/report.txt" --remote --env-file=.env.management
npx wrangler r2 bucket delete "$BUCKET" --env-file=.env.management

Remote wurde nur documents/report.txt erstellt. Falls weitere Objekte vorhanden sind, prüfen Sie diesen exakten Bucket und klären Sie vor dem Entfernen deren Eigentümerschaft.

Aktualisieren Sie die Worker- und Bucket-Listen im Dashboard und führen Sie die Plattformprüfung zur Bereinigung aus. Authentifizierungs- oder Netzwerkfehler sind nicht eindeutig und gelten nicht als erfolgreiche Löschung.

Verbleibende Zugangsdaten schließen

In diesem Schritt widerrufen Sie auf der Seite „API Tokens“ Ihres Profils das Verwaltungstoken dieses Labs, entfernen das lokale Anwendungsgeheimnis und beenden die Autorisierung der VM. Tun Sie dies erst, nachdem die vorherige Bereinigungsprüfung erfolgreich war.

rm .env.management .dev.vars
unset ACCESS_TOKEN
npx wrangler logout
npx wrangler whoami --json || true

Verlangen Sie loggedIn: false. Der Widerruf des Verwaltungstokens ist ein separater manueller Prüfschritt im Dashboard. Das Löschen der lokalen Datei allein widerruft das Token nicht. Lassen Sie die normale Dashboard-Anmeldung und die Tokens anderer Labs unverändert.

Zusammenfassung

Verwenden Sie R2-Metadaten für bedingte Antworten, übertragen Sie einzelne Bytebereiche, behandeln Sie nicht erfüllbare Anforderungen und bereinigen Sie den privaten Download-Dienst.