Introdução
Sua equipe de suporte precisa de um pequeno repositório de documentos. Um objeto é formado pelos bytes de um arquivo e seus metadados; um bucket agrupa objetos, e uma chave é o nome completo de um objeto. As barras nas chaves criam prefixos úteis, mas não criam diretórios comuns de um sistema de arquivos. Você criará um bucket privado, fará upload de dois documentos sintéticos, inspecionará os metadados, baixará os bytes exatos e removerá apenas o documento selecionado antes de fazer a limpeza.
Conclua primeiro Conecte o LabEx à sua conta Cloudflare. Esse laboratório ensina a usar o terminal do LabEx, a autorização do dispositivo, a confirmação da conta de aprendizado e a configuração do ID da conta. Este laboratório começa de forma independente em /home/labex/project/r2-lab, com Node.js 22.22.0, Wrangler 4.131.1 e AWS SDK 3.888.0 já preparados. No seu próprio computador, instale primeiro o Node.js e depois instale o Wrangler e o AWS SDK como dependências do projeto com npm install.
Antes de começar: sua conta de aprendizado precisa ter uma assinatura R2 ativa. A página de configuração do R2 da Cloudflare inclui um fluxo de checkout; consulte-a se o R2 não estiver ativo. Uma conta Free não ativa o R2 automaticamente. Leia a página de preços para entender as cobranças de armazenamento e operações. Este exercício usa arquivos sintéticos pequenos e não exige um domínio comprado. Você precisa de permissão para gerenciar buckets e de permissão para criar um token R2 de usuário limitado a este novo bucket. Mantenha o acesso público desativado. Nunca cole credenciais nesta lição, no chat ou em capturas de tela.
Crie seu bucket privado de documentos
Nesta etapa, você autoriza esta VM e cria um bucket descartável. A autorização do dispositivo confirma sua conta de aprendizado. O gerenciamento de buckets R2 usa um token de API separado, restrito a essa conta.
Inicie o Bash para usar a sintaxe dos comandos abaixo. Depois, acesse o projeto preparado e verifique as ferramentas. Mantenha este mesmo terminal aberto para que as variáveis com os nomes dos recursos continuem disponíveis:
bash
cd /home/labex/project/r2-lab
export PATH="$PWD/.tools/node-v22.22.0-linux-x64/bin:$PATH"
node --version
npx wrangler --version
Autorize o código do dispositivo exibido usando seu próprio navegador. Confirme a conta de aprendizado e os escopos de leitura solicitados para a conta e o usuário antes de conceder o consentimento:
npx wrangler login --device --browser=false --scopes account:read user:read
npx wrangler whoami --json
Exija loggedIn: true. Leia o nome da conta mesmo que apenas uma conta seja listada. Substitua YOUR_ACCOUNT_ID abaixo pelo ID real de 32 caracteres dessa conta. openssl rand -hex 6 gera doze caracteres hexadecimais aleatórios para evitar conflitos com uma execução anterior deste laboratório. O here-document grava um arquivo de configuração padrão; o shell substitui nele os valores das suas variáveis.
ACCOUNT_ID=YOUR_ACCOUNT_ID
RUN_ID=$(openssl rand -hex 6)
NAME="labex-c05-r01-$RUN_ID"
BUCKET="$NAME-docs"
cat > wrangler.jsonc <<JSON
{"name":"$NAME","account_id":"$ACCOUNT_ID","compatibility_date":"2026-07-30","r2_buckets":[{"binding":"DOCUMENTS","bucket_name":"$BUCKET"}]}
JSON
Para gerenciar buckets, abra a página API Tokens do seu perfil da Cloudflare e crie um token personalizado com o nome deste laboratório. Conceda Account → Workers R2 Storage → Edit e restrinja Account Resources à conta de aprendizado cujo ID você salvou. Defina uma expiração curta. Não inclua outras contas nem permissões não relacionadas. Essa permissão no nível da conta cria e exclui buckets; o token limitado a objetos da próxima etapa não pode fazer isso.
Copie o token uma única vez no prompt oculto desta VM. umask 077 restringe o arquivo ao seu usuário; read -s oculta a entrada. O arquivo usa a variável de token padrão do Wrangler e é excluído do Git.
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
Use --env-file=.env.management somente nos comandos de gerenciamento do R2; o whoami comum continuará verificando a autorização do dispositivo na VM.
Coloque --env-file no final de cada comando do Wrangler para que a lista de argumentos de arquivos não inclua o nome do comando. Depois de criar cada bucket, se o Wrangler perguntar se deve adicionar uma vinculação à configuração, digite n e pressione Enter. A configuração já contém a vinculação necessária.
npx wrangler r2 bucket create "$BUCKET" --env-file=.env.management
Liste seus buckets e encontre o nome exato gerado. Outros buckets pertencem a outros trabalhos; não os altere.
npx wrangler r2 bucket list --env-file=.env.management
No Dashboard, abra Storage & databases → R2 → Overview, selecione exatamente este bucket e verifique se a lista de objetos está vazia. Nas configurações dele, mantenha desativados o URL público de desenvolvimento e os domínios personalizados. O nome do bucket no Dashboard confirma a identidade; as verificações de download posteriores comprovarão os bytes armazenados.

O exemplo mostra armazenamento Standard e Public Access Disabled. O nome do bucket gerado será diferente.
Faça upload dos documentos com metadados
Nesta etapa, você concede ao SDK acesso somente a este bucket e armazena dois documentos. Content type informa a um cliente como interpretar os bytes; custom metadata armazena pequenos rótulos definidos por você junto ao objeto. Nenhum dos dois é uma regra de controle de acesso.
A API compatível com S3 permite que SDKs de armazenamento padrão acessem o R2. Ela usa um par de chave de acesso diferente do token de dispositivo do Wrangler. Em R2 Overview, use Account Details → Manage API Tokens e crie um User API token com o nome do recurso gerado para este laboratório. Escolha Object Read & Write, restrinja o token exatamente a este novo bucket e selecione uma expiração curta, se o formulário oferecer essa opção. Não escolha todos os buckets nem o acesso Admin. Mantenha esta página do token disponível até armazenar o segredo, que será exibido uma única vez.
Use os prompts Bash a seguir na VM. read -s oculta a entrada; umask 077 faz com que o arquivo de credenciais possa ser lido somente pelo seu usuário. Esses nomes são as variáveis de ambiente padrão do AWS SDK. Cole o Access Key ID e o Secret Access Key nos respectivos prompts e pressione Enter. Não cole o valor do token de API geral.
No formulário do token, selecione 24 hours em TTL e confira o bucket exato e a permissão Object Read & Write antes de criá-lo. Revogue o token ao terminar o laboratório; a expiração é apenas uma proteção adicional.
umask 077
read -r -s -p 'Access Key ID: ' AWS_ACCESS_KEY_ID; printf '\n'
read -r -s -p 'Secret Access Key: ' AWS_SECRET_ACCESS_KEY; printf '\n'
printf 'AWS_ACCESS_KEY_ID=%s\nAWS_SECRET_ACCESS_KEY=%s\n' "$AWS_ACCESS_KEY_ID" "$AWS_SECRET_ACCESS_KEY" > .env.s3
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY
Grave um cliente reutilizável do SDK padrão. O SDK exige uma string de região; no R2, use auto. A leitura da configuração existente mantém as operações da CLI e do SDK direcionadas à mesma conta e ao mesmo bucket.
cat > storage.mjs <<'JS'
import { S3Client } from "@aws-sdk/client-s3";
import { readFileSync } from "node:fs";
const config = JSON.parse(readFileSync("wrangler.jsonc", "utf8"));
export const Bucket = config.r2_buckets[0].bucket_name;
export const s3 = new S3Client({
region: "auto",
endpoint: `https://${config.account_id}.r2.cloudflarestorage.com`,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
}
});
JS
Agora crie o programa de upload. PutObjectCommand armazena os bytes na chave informada. Os dois documentos são sintéticos; o manual mantido comprovará que uma exclusão seletiva posterior não apaga chaves não relacionadas.
cat > upload.mjs <<'JS'
import { PutObjectCommand } from "@aws-sdk/client-s3";
import { readFileSync } from "node:fs";
import { s3, Bucket } from "./storage.mjs";
await s3.send(new PutObjectCommand({
Bucket, Key: "documents/report.txt", Body: readFileSync("document.txt"),
ContentType: "text/plain", Metadata: { team: "blue", revision: "1" }
}));
await s3.send(new PutObjectCommand({
Bucket, Key: "retained/handbook.txt", Body: readFileSync("retained.txt"),
ContentType: "text/plain"
}));
console.log("Uploaded two synthetic documents");
JS
--env-file carrega os valores das credenciais sem exibi-los:
node --env-file=.env.s3 upload.mjs
A linha de sucesso é exibida somente depois que as duas chamadas de API aguardadas forem concluídas. A verificação da plataforma lê os objetos e os metadados reais de forma independente.
Liste os metadados e compare os bytes baixados
Nesta etapa, você inspeciona as chaves sem baixar todos os objetos e depois recupera o relatório. ListObjectsV2 lista as chaves, enquanto HeadObject recupera somente os metadados. Este bucket pequeno cabe em uma única página de listagem; em produção, as listagens precisam seguir os tokens de continuação quando IsTruncated for true.
cat > inspect.mjs <<'JS'
import { ListObjectsV2Command, HeadObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3";
import { writeFileSync } from "node:fs";
import { s3, Bucket } from "./storage.mjs";
const page = await s3.send(new ListObjectsV2Command({ Bucket }));
console.log(page.Contents.map(object => object.Key));
const metadata = await s3.send(new HeadObjectCommand({ Bucket, Key: "documents/report.txt" }));
console.log({ contentType: metadata.ContentType, metadata: metadata.Metadata });
const object = await s3.send(new GetObjectCommand({ Bucket, Key: "documents/report.txt" }));
writeFileSync("download.txt", await object.Body.transformToByteArray());
JS
node --env-file=.env.s3 inspect.mjs
A lista contém documents/report.txt e retained/handbook.txt. O relatório tem text/plain, team: blue e revision: 1. A ordem de exibição dos metadados pode variar.
cmp compara os bytes e não exibe nada quando os arquivos são iguais. A mensagem a seguir aparece somente se a comparação for bem-sucedida:
cmp document.txt download.txt && printf "Downloaded bytes match\n"
Atualize a lista de objetos do mesmo bucket no Dashboard e abra os detalhes do relatório. Compare a chave e o tipo de conteúdo com a saída do SDK. Use a saída da CLI como evidência dos metadados se o Dashboard atual não exibir um campo de metadados personalizados.

O exemplo mostra text/plain, revision 1, team blue e a prévia do relatório sintético. O nome do bucket e a data de criação serão diferentes.
Remova somente o relatório selecionado
Nesta etapa, você exclui uma chave de objeto completa e mantém o manual. Um prefixo não é um diretório que possa ser removido recursivamente; passe exatamente a chave do relatório para a API.
cat > remove-report.mjs <<'JS'
import { DeleteObjectCommand, ListObjectsV2Command } from "@aws-sdk/client-s3";
import { s3, Bucket } from "./storage.mjs";
await s3.send(new DeleteObjectCommand({ Bucket, Key: "documents/report.txt" }));
const page = await s3.send(new ListObjectsV2Command({ Bucket }));
console.log(page.Contents.map(object => object.Key));
JS
node --env-file=.env.s3 remove-report.mjs
Somente retained/handbook.txt permanece. A verificação da plataforma também baixa o manual para confirmar que seu conteúdo continua inalterado. Execute essa verificação antes de prosseguir para a limpeza completa.
Limpe o bucket sob sua responsabilidade
Nesta etapa, você remove o objeto restante e depois o bucket vazio. Mantenha suas credenciais ativas até confirmar a exclusão remota.
cat > cleanup.mjs <<'JS'
import { DeleteObjectCommand, ListObjectsV2Command } from "@aws-sdk/client-s3";
import { s3, Bucket } from "./storage.mjs";
await s3.send(new DeleteObjectCommand({ Bucket, Key: "retained/handbook.txt" }));
const page = await s3.send(new ListObjectsV2Command({ Bucket }));
console.log("Remaining objects:", page.KeyCount);
JS
node --env-file=.env.s3 cleanup.mjs
Exija Remaining objects: 0. Se você abriu um novo terminal, leia o nome gerado do bucket na configuração; node -p imprime esse único campo.
BUCKET=$(node -p "JSON.parse(require('fs').readFileSync('wrangler.jsonc')).r2_buckets[0].bucket_name")
npx wrangler r2 bucket delete "$BUCKET" --env-file=.env.management
Quando solicitado, confirme somente o bucket exato deste laboratório. Liste os buckets novamente; a listagem bem-sucedida não deve conter esse nome. Um erro de autenticação ou de rede não comprova a exclusão.
npx wrangler r2 bucket list --env-file=.env.management
Atualize a mesma lista no Dashboard e execute a verificação da plataforma desta etapa enquanto ainda estiver conectado.
Revogue a credencial do laboratório e saia da sessão
Nesta etapa, você encerra o acesso deixado por este exercício. Na página R2 API Tokens, revogue somente o token de objeto com o nome deste laboratório. Na página API Tokens do seu perfil, revogue o token de gerenciamento do R2 separado que você criou para este laboratório. Excluir um bucket não revoga um token, e fazer logout do Wrangler não revoga as credenciais do S3.
Depois da revogação, remova o arquivo de credenciais local e faça logout desta VM:
rm .env.s3 .env.management
npx wrangler logout
Inspecione a identidade estruturada. O status diferente de zero é esperado quando você está desconectado:
npx wrangler whoami --json || true
Exija loggedIn: false e mantenha o login comum do Dashboard. As verificações da plataforma confirmam a remoção das credenciais locais e o logout do Wrangler. As duas revogações de token são etapas manuais no Dashboard neste laboratório; elas não são inferidas pela exclusão dos arquivos.
Resumo
Você criou um bucket R2 privado, armazenou bytes e metadados de objetos, listou e baixou documentos, comprovou uma exclusão seletiva e removeu o acesso ao bucket.



