{
  "id": "add-cloudflare-r2-file-upload",
  "type": "prompts",
  "category": "prompts",
  "locale": "pt",
  "url": "/pt/prompts/add-cloudflare-r2-file-upload",
  "title": "Prompt para Adicionar Upload de Arquivo com Cloudflare R2",
  "description": "Prompt de IA para copiar e colar que adiciona URLs de upload pré-assinadas do Cloudflare R2 a um projeto Next.js App Router com validação no servidor.",
  "tools": [
    "Cursor",
    "Claude Code",
    "Codex",
    "Windsurf"
  ],
  "stack": [
    "Next.js",
    "Cloudflare",
    "TypeScript"
  ],
  "tags": [
    "cloudflare",
    "nextjs",
    "typescript",
    "build"
  ],
  "difficulty": "medium",
  "updated": "2026-06-08",
  "markdown": "Forneça este prompt ao seu agente para implementar uploads diretos para o R2 usando URLs\npré-assinadas — mantendo os segredos no lado do servidor e evitando o erro comum de rotear os bytes do\narquivo através do servidor Next.js.\n\n## Prompt Principal\n\n```txt title=\"Main Prompt\"\nYou are working in a Next.js App Router project using TypeScript.\n\nTask: add Cloudflare R2 file upload via presigned PUT URLs.\n\nRequirements:\n- Install `@aws-sdk/client-s3` and `@aws-sdk/s3-request-presigner` (R2 is S3-compatible).\n- Create a Server Action in `src/lib/actions/get-upload-url.ts` that:\n  - Accepts `{ filename: string; contentType: string; sizeBytes: number }`.\n  - Validates: `sizeBytes` must be <= 10_485_760 (10 MB); `contentType` must start with `image/`.\n  - Uses `PutObjectCommand` + `getSignedUrl` to generate a 60-second presigned URL.\n  - Returns `{ uploadUrl: string; publicUrl: string; key: string }`.\n- Store credentials in `.env`: `R2_ACCOUNT_ID`, `R2_ACCESS_KEY_ID`, `R2_SECRET_ACCESS_KEY`,\n  `R2_BUCKET_NAME`, `R2_PUBLIC_URL`.\n- Create a client component `src/components/FileUploader.tsx` that:\n  - Renders a file `<input accept=\"image/*\" />`.\n  - On change: calls the Server Action to get the presigned URL, then does a `fetch` PUT directly\n    to R2, then shows the `publicUrl` as a preview.\n  - Shows upload progress via `XMLHttpRequest` `progress` events (not fetch, which lacks progress).\n- Do not add any other dependencies. Do not modify `next.config.ts` unless required for image domains.\n\nStop and list all planned file changes before writing any code.\n```\n\n## Notas de Implementação\n\n- O R2 usa o SDK S3 com `endpoint: https://<accountId>.r2.cloudflarestorage.com` e\n  `region: 'auto'`. Esquecer `region: 'auto'` causa um erro de assinatura.\n- URLs pré-assinadas são geradas no lado do servidor; o cliente nunca vê `R2_SECRET_ACCESS_KEY`.\n- Para acesso público ao bucket, ative a opção \"Acesso público\" do bucket R2 no painel do Cloudflare\n  e forneça a URL `r2.dev` resultante como `R2_PUBLIC_URL`.\n\n## Alterações de Arquivos Esperadas\n\n```txt\nsrc/lib/actions/get-upload-url.ts      (new)\nsrc/lib/r2.ts                          (new — S3Client singleton)\nsrc/components/FileUploader.tsx        (new — Client Component)\n.env.example                           (edited)\npackage.json                           (edited)\nnext.config.ts                         (edited — add r2.dev to images.remotePatterns)\n```\n\n## Critérios de Aceitação\n\n- Um arquivo selecionado em `FileUploader` aciona uma busca por URL pré-assinada e, em seguida, faz upload diretamente para o R2.\n- Arquivos maiores que 10 MB são rejeitados antes de qualquer requisição de rede.\n- Tipos MIME que não são imagens são rejeitados no lado do servidor e retornam um erro equivalente a 400.\n- A prévia da URL pública renderiza a imagem enviada após a conclusão do upload.\n\n## Comandos de Teste\n\n```bash\nbun run typecheck\nbun run dev\n# upload a PNG < 10 MB via the component and confirm it appears in R2 bucket\n# attempt a 15 MB file and confirm rejection\n# attempt a .pdf and confirm rejection\n```\n\n## Erros Comuns de IA\n\n- Definir `region: 'us-east-1'` em vez de `'auto'`, causando erros `SignatureDoesNotMatch`.\n- Rotear os bytes do arquivo através da Server Action do Next.js em vez de fazer fetch diretamente para o R2.\n- Esquecer de chamar `'use server'` no topo do arquivo de ações.\n- Usar `fetch` para o upload e perder eventos de progresso.\n\n## Prompt de Correção\n\n```txt title=\"Fix Prompt\"\nThe upload fails with `SignatureDoesNotMatch` or the file routes through the server.\nFix in order:\n1. In `src/lib/r2.ts`, set `region: 'auto'` and `endpoint: https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com`.\n2. In `FileUploader.tsx`, use `XMLHttpRequest` with a `progress` event to PUT directly to the\n   presigned URL — not fetch, and not a Server Action for the actual upload bytes.\nShow corrected diff only.\n```"
}