Skip to main content

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

  1. A ACL está sendo definida corretamente na presigned URL?

    • A URL gerada contém o parâmetro x-amz-acl=public-read.
  2. O bucket está configurado corretamente?

    • O bucket pode estar com políticas que impedem a criação de objetos públicos por padrão.
  3. 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.

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

  1. 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.
  2. 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.