Problemas no Upload de Arquivos Públicos com Presigned URL utilizando SDK
Problema
Ao utilizar a SDK do S3 para gerar uma presigned URL para upload de arquivos com a ACL configurada como public-read
, os arquivos enviados ainda aparecem como privados, mesmo que o bucket esteja público.
Diagnóstico
-
A ACL está sendo definida corretamente na presigned URL?
- A URL gerada contém o parâmetro
x-amz-acl=public-read
.
- A URL gerada contém o parâmetro
-
O bucket está configurado corretamente?
- O bucket pode estar com políticas que impedem a criação de objetos públicos por padrão.
-
O upload do arquivo está incluindo os headers corretos?
- A presigned URL apenas autentica a requisição, mas os headers enviados no momento do
PUT
são fundamentais.
- A presigned URL apenas autentica a requisição, mas os headers enviados no momento do
Solução
Ajustando o Código
A chave para garantir que o arquivo seja enviado já como público é incluir os headers corretos na requisição PUT.
Exemplo de Código
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
const { getSignedUrl } = require('@aws-sdk/s3-request-presigner');
const fs = require('fs');
const path = require('path');
const region = 'br-se1';
const endpoint = `https://${region}.magaluobjects.com`;
const bucketName = 'nome-do-seu-bucket';
const key = 'key';
const accessKeyId = 'accessKeyId';
const secretAccessKey = 'secretAccessKey';
const s3Client = new S3Client({
region,
endpoint,
credentials: {
accessKeyId,
secretAccessKey,
}
});
async function generatePresignedUrl() {
const command = new PutObjectCommand({
Bucket: bucketName,
Key: key,
ACL: 'public-read', // ACL public-read
});
const signedUrl = await getSignedUrl(s3Client, command, { expiresIn: 3600 });
return signedUrl;
}
async function uploadFileWithPresignedUrl() {
const filePath = path.join(__dirname, key);
const fileStream = fs.createReadStream(filePath);
const presignedUrl = await generatePresignedUrl();
console.log('URL pré-assinada gerada:', presignedUrl);
const fetch = require('node-fetch');
const response = await fetch(presignedUrl, {
method: 'PUT',
body: fileStream,
// Requests de PUT com presigned necessitam desses dois headers para a assinatura
headers: {
'Host': new URL(presignedUrl).host, // Header Host a partir da URL
'x-amz-acl': 'public-read', // Definindo a ACL como public-read
}
});
// Baixar o arquivo e validar se o mesmo está público
const parsedUrl = new URL(presignedUrl);
const publicUrl = parsedUrl.origin + parsedUrl.pathname;
const responseGet = await fetch(publicUrl, { method: 'GET' });
if (response.ok) {
console.log('Arquivo enviado com sucesso!');
} else {
console.error('Falha ao enviar o arquivo:', response.statusText);
}
if (responseGet.ok) {
console.log('Arquivo está público!');
} else {
console.error('Falha ao baixar o arquivo:', responseGet.status);
}
}
// Chama a função para fazer o upload
uploadFileWithPresignedUrl().catch(console.error);
Explicação
-
Inclusão do header
x-amz-acl: public-read
na requisição PUT- Sem esse header, a ACL do objeto pode ser sobrescrita pela configuração padrão do bucket.
-
Verificação da acessibilidade do objeto após o upload
- Após o upload, a URL pública é testada para garantir que o arquivo pode ser acessado publicamente.
Versões utilizadas
-
@aws-sdk/client-s3
: 3.677.0 -
@aws-sdk/s3-request-presigner
: 3.677.0
Conclusão
Esse problema ocorre porque a ACL fornecida na presigned URL não é automaticamente aplicada no upload. Ao garantir que o header x-amz-acl: public-read
seja incluído na requisição PUT, o objeto será corretamente armazenado como público. Além disso, é importante validar se a política do bucket não está bloqueando uploads públicos de objetos.