P PasteCode
Playbook

Prompt-zu-PR: Cloudflare R2 Dateiupload hinzufügen

Ende-zu-Ende SOP zum Einbinden vorab signierter R2-Uploads in eine Next.js- oder Cloudflare Workers-App – Bucket-Bindung, vorab signierte URLs und Client-Upload-Ablauf.

CursorClaude CodeCodexWindsurf Next.jsCloudflareTypeScript
.md .json Schwierigkeit: Mittel Aktualisiert 8. Juni 2026

Datei-Upload zu R2 hinzufügen, ohne Binärdaten über Ihren Server zu proxen. Der Agent generiert eine vorab signierte URL serverseitig; der Browser sendet per PUT direkt an R2.

1. Anforderung

Benutzer können Dateien (Bilder, PDFs, bis zu 10 MB) über eine Drag-and-Drop-Oberfläche hochladen. Der Server stellt eine vorab signierte PUT-URL aus; der Browser streamt die Datei direkt zu Cloudflare R2. Nach dem Upload wird eine öffentliche Lese-URL zurückgegeben. Keine Dateien passieren den Next.js-Server.

2. Erster Prompt

First Prompt
Add Cloudflare R2 file upload to this Next.js 15 App Router project.
Requirements:
- Use presigned PUT URLs (not proxy upload). The Next.js route only issues
the presigned URL; the browser uploads directly to R2.
- Install `@aws-sdk/client-s3` and `@aws-sdk/s3-request-presigner`
(R2 is S3-compatible).
- Create `src/app/api/upload/presign/route.ts` (POST). Accept JSON body
`{ filename: string; contentType: string; size: number }`. Validate:
allowed MIME types (image/jpeg, image/png, image/webp, application/pdf),
max size 10 MB. Return `{ uploadUrl, publicUrl, key }`.
- Read R2 credentials from env vars:
R2_ACCOUNT_ID, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY,
R2_BUCKET_NAME, NEXT_PUBLIC_R2_PUBLIC_URL.
- Create `src/components/FileUpload.tsx` — a Client Component with a
drag-and-drop zone. On file select: POST to /api/upload/presign, then
PUT the file to the returned uploadUrl with the correct Content-Type header.
Show progress, handle errors, and call an `onUpload(publicUrl)` callback.
- Do not store the file in any database table; that is the caller's
responsibility via the onUpload callback.

3. Erwartete Dateiänderungen

package.json (@aws-sdk/client-s3, @aws-sdk/s3-request-presigner)
src/app/api/upload/presign/route.ts (new — presign endpoint)
src/components/FileUpload.tsx (new — drag-and-drop client component)
src/lib/r2.ts (new — S3Client singleton)
.env.local.example (R2_* vars)

4. Checkliste zur Überprüfung

  • Der Presign-Endpunkt validiert MIME-Typ und Größe, bevor R2 aufgerufen wird – schlechte Uploads werden vor einem AWS-Aufruf abgelehnt.
  • Der S3Client in src/lib/r2.ts verwendet endpoint: https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com und region: "auto".
  • Die Gültigkeit der vorab signierten URL ist kurz (60–300 Sekunden); nicht ein ganzer Tag.
  • Der PUT vom Browser enthält einen Content-Type-Header, der mit dem beim Signieren verwendeten übereinstimmt – Abweichungen verursachen 403.
  • NEXT_PUBLIC_R2_PUBLIC_URL zeigt auf die öffentliche Domain des Buckets, nicht auf den R2-API-Endpunkt.
  • FileUpload.tsx beginnt mit "use client" – keine Server-Imports.
  • Größen- oder Typüberprüfung erfolgt nicht nur clientseitig – der Server muss ebenfalls validieren.
  • Der Schlüssel verwendet ein zufälliges Präfix (z. B. crypto.randomUUID()), um Dateinamen-Kollisionen zu vermeiden.

5. Testbefehle

Terminal window
# Start dev server
bun dev
# Test presign endpoint directly
curl -X POST http://localhost:3000/api/upload/presign \
-H "Content-Type: application/json" \
-d '{"filename":"test.png","contentType":"image/png","size":12345}' | jq .
# Confirm returned uploadUrl is an R2 presigned URL (contains X-Amz-Signature)
# Then PUT a real file to confirm end-to-end
curl -X PUT "<uploadUrl>" \
-H "Content-Type: image/png" \
--data-binary @test.png -v
# Fetch the public URL to verify the file is readable
curl -I "<publicUrl>"

6. Häufige Fehler

  • 403 bei PUT – Der Content-Type-Header im Browser-PUT stimmt nicht mit dem beim Signieren verwendeten überein. Stellen Sie sicher, dass beide exakt dieselbe Zeichenfolge verwenden.
  • NoSuchBucket – Falscher Bucket-Name oder Account-ID. Überprüfen Sie R2_BUCKET_NAME im Cloudflare-Dashboard.
  • InvalidAccessKeyId – Der R2-API-Token benötigt die Berechtigung “Object Read & Write”, nicht nur “Read”.
  • CORS-Fehler bei direktem PUT – Die CORS-Richtlinie des R2-Buckets muss PUT von Ihrer Ursprungsseite erlauben. Stellen Sie dies im Cloudflare-Dashboard unter R2 → Einstellungen → CORS ein.
  • Agent verwendet @aws-sdk/s3-presigned-post (für POST) anstelle von getSignedUrl für PUT – anderer Ablauf, anderer Client-Code erforderlich.

7. Fix-Prompt

Fix Prompt
The browser PUT to R2 returns 403 SignatureDoesNotMatch.
The Content-Type passed to getSignedUrl must exactly match the Content-Type
header sent by the browser. Update the presign route to pass the contentType
from the request body into getSignedUrl, and update FileUpload.tsx to set
the Content-Type header on the PUT request to the same value.
Also confirm the S3Client endpoint is:
https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com
not the generic AWS S3 endpoint.

8. PR-Beschreibung

PR description
## Feature: Cloudflare R2 file upload via presigned URLs
- New POST `/api/upload/presign` validates MIME type and size, then returns
a short-lived R2 presigned PUT URL
- Files upload directly from the browser to R2 — zero binary data through
the Next.js server
- New `<FileUpload>` component: drag-and-drop, progress indicator, error state
- Random UUID key prefix prevents filename collisions
**Required env vars** (see `.env.local.example`):
`R2_ACCOUNT_ID`, `R2_ACCESS_KEY_ID`, `R2_SECRET_ACCESS_KEY`,
`R2_BUCKET_NAME`, `NEXT_PUBLIC_R2_PUBLIC_URL`
**R2 bucket setup**: enable public access and add a CORS rule allowing PUT
from your app origin.