Upload de Arquivos no R2 da Cloudflare com Signed URLs e GraphQL.
Dizem que, se queremos aprender algo, é só colocar de forma errada na internet que sempre aparece alguém te xingando e corrigindo. 😅
Então, vou mostrar como fiz o upload de arquivos para o R2 usando signed URLs e uma API GraphQL. Essa é uma aplicação que eu fiz, e agora precisei implementar o upload de arquivos, algo que nunca tinha feito antes.
Para simplificar, não vou mostrar tratamentos de erros, controles de carregamento, etc... Mas pode ficar tranquilo que eles existem, viu? 😂
Etapa 1: (Aplicação) Solicitar uma URL Assinada
- A aplicação envia uma requisição para a API solicitando a URL assinada.
- Incluímos os dados necessários no corpo da requisição ou nos parâmetros, como:
- Tipo de conteúdo (ex.:
image/jpeg
) - Nome do arquivo (ex.:
relatorio.jpeg
)
- Tipo de conteúdo (ex.:
Exemplo de Requisição:
Como podemos fazer upload de múltiplos arquivos, utilizamos Promise.all
para executar as requisições de forma paralela. Para cada arquivo, enviamos o tipo e o nome, permitindo que a API gere uma URL assinada específica para cada um.
const [generateSignedUrl] = useMutation(CREATED_URL_SIGNED)
const { data } = await generateSignedUrl({
variables: { contentType: filetype, key: fileName },
})
const signedUrls = await Promise.all(
fileList.map(async (file) => {
const { data } = await generateSignedUrl({
variables: { contentType: file.type, key: file.name },
})
return { file, signedUrl: data.generateSignedUrl }
}))
Etapa 2: (API) Gerar a URL assinada com o token.
- Na API, receba os dados da solicitação e valide o nome do arquivo e tipo de conteúdo, garantindo que são permitidos.
- Utilize as credenciais da Cloudflare para acessar o R2 e gerar a URL assinada.
- Configure os seguintes parâmetros:
- Método: PUT (para upload do arquivo)
- Tempo de expiração da URL (ex.: 15 minutos)
- Retorne a URL assinada.
//cliente r2
export const r2 = new S3Client({
region: 'auto',
endpoint: process.env.CLOUDFLARE_ENDPOINT,
credentials: {
accessKeyId: process.env.CLOUDFLARE_ACCESS_KEY_ID,
secretAccessKey: process.env.CLOUDFLARE_SECRET_ACCESS_KEY,
},
})
//resolver
export default {
Mutation: {
generateSignedUrl: async (_, { fileType, contentType }) => {
try {
const command = new PutObjectCommand({
Bucket: process.env.CLOUDFLARE_BUCKET,
Key: fileType,
ContentType: contentType,
});
const signedUrl = await getSignedUrl(r2, command, { expiresIn: 3600 });
return signedUrl;
} catch (error) {
console.error('Erro ao gerar URL assinada:', error);
throw new Error('Não foi possível gerar a URL assinada.');
}
},
}}
//schema
type Mutation {
generateSignedUrl( key: String!, contentType: String!): String!
}
Se tudo der certo, a API retornará uma URL assinada que pode ser usada para fazer o upload do arquivo para o bucket S3.
Etapa 3: (Aplicação) Realizar o upload do arquivo usando a URL assinada.
- Após receber a URL assinada da API, use-a para fazer o upload do arquivo diretamente para o R2.
- Utilize fetch ou outra biblioteca HTTP para enviar o arquivo no método PUT.
- Inclua o cabeçalho correto para o tipo de conteúdo.
await Promise.all(
signedUrls.map(({ file, signedUrl }) =>
axios.put(signedUrl, file, {
headers: { 'Content-Type': file.type },
}),
),
)
Pronto, os arquivos estão salvos. O downloader fica para outra hora. Hoje, a aplicação não tem a necessidade.