Aplicação web com upload direto para S3 e SQS

Exemplo prático de como usar a SDK em JavaScript da AWS para fazer upload direto de arquivos, a partir do navegador para o serviço Amazon Simple Storage (S3). A principal vantagem de fazer um upload direto para o S3, sem passar por algum tipo de servidor intermediário é diminuir o gargalo que um pré-processamento poderia gerar.

Este exemplo foi apresentado como atividade na disciplina de Tópicos em Engenharia de Software lecionada pelo Dr. Gabriel Costa Silva. Os códigos de upload são idênticos ao exemplo já disponível pela própria Amazon (https://docs.aws.amazon.com/pt_br/sdk-for-javascript/v2/developer-guide/s3-example-photo-album.html), só que ao invés de ser um álbum de fotos, esse sistema funciona como se fosse um simples gerenciador de arquivos apelidado de “cenourinha files”.

Screenshot da Aplicação
Screenshot da Aplicação

Os objetivos para esse exemplo são:

  • Ter uma página web estática hospedada na S3 que permita o upload de arquivos,
  • Um bucket para hospedar os arquivos (Eu usei o mesmo bucket da aplicação),
  • E uma fila (SQS) que receba uma mensagem toda vez que ocorra um upload.

Todo o código alterado do exemplo da AWS está neste repositório: https://github.com/danillolima/aws-js-upload-to-s3-sqs

Primeiro passo é criar o(os) bucket no S3.

O bucket que recebe os arquivos deve ter uma política que permita get/put publicamente. Exemplo:

{
    "Version": "2012-10-17",
    "Id": "ID_PARA_SUA_POLITICA",
    "Statement": [
        {
            "Sid": "ID_PARA_SEU STATEMENT",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": "arn:aws:s3:::NOME_DO_BUCKET/*"
        }
    ]
}

A seguinte política de CORS para o bucket:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedMethod>DELETE</AllowedMethod>
    <AllowedMethod>HEAD</AllowedMethod>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

Depois de criar seu bucket você precisa criar uma função no serviço “Cognito” da AWS para sua aplicação, após criar a função você deve usar o IAM para dar permissões ou anexar políticas para aquela função fazer alterações nos seus recursos.

ID do grupo que deve ser utilizado no aplicativo

No app.js você deve mudar os dados:

  
var bucketName = "NOME_DO_BUCKET";
var bucketRegion = "us-east-1"; //Mudar para região do seu bucket
var IdentityPoolId = "ID_DA_FUNCAO_NO_COGNITO";
//...
var params = {
  QueueName: 'NOME_DA_SUA_FILA'
};

Para a fila usando SQS, a política deve permitir apenas o envio de mensagens, exemplo:

{
  "Version": "2012-10-17",
  "Id": "ID_DA_SUA_POLITICA",
  "Statement": [
    {
      "Sid": "ID_DO_SEU_STATEMENT",
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": "SQS:SendMessage",
      "Resource": "ARN_DA_FILA",
      "Condition": {
        "ArnLike": {
          "aws:SourceArn": "arn:aws:s3:*:*:NOME_DO_BUCKET_S3_COM_APP"
        }
      }
    }
  ]
}

*Essa política é desnecessária se você habilitar o acesso completo da fila para o usuário do Cognito.

Em teoria você já deve ter a aplicação “cenourinha files” rodando, apesar de ter faltado alguns passos mais detalhados sobre a configuração da AWS, principalmente no que diz respeito a criação e atribuições de políticas, se você tiver dúvidas ou dificuldades e sugestões deixe um comentário abaixo.

O código do SDK em JS da AWS é bem intuitivo, abaixo por exemplo é o trecho usado para enviar mensagens com a url e o bucket do arquivo após o upload ter sido completado. É usado dois métodos, um para captar a url da fila e outro para enviar a mensagem a cada upload.

var params = {
  QueueName: 'cenourinhas'
};

var queueURL;
sqs.getQueueUrl(params, function(err, data) {
  if (err) {
    console.log("Error", err);
  } else {
    console.log("Success", data.QueueUrl);
    queueURL = data.QueueUrl;
  }
});
  var promise = upload.promise();
  promise.then(
    function(data) {
      console.log(data)
      var params = {
        DelaySeconds: 10,
        MessageAttributes: {},
        MessageBody: JSON.stringify ({'url' : data.Location,
                      'bucket' : data.Bucket}),
        // MessageDeduplicationId: "TheWhistler",  // Required for FIFO queues
        // MessageId: "Group1",  // Required for FIFO queues
        QueueUrl: queueURL
      };
      sqs.sendMessage(params, function(err, data) {
        if (err) {
          console.log("Error", err);
        } else {
          console.log("Success", data.MessageId);
        }
      });
      alert("Arquivo salvo");
      viewFolder(folderName);
    },
    function(err) {
      return alert("Houve um erro fazendo upload do seu arquivo: ", err.message);
    }
  );
}

Nesse código mais extenso, quando a promise é concluída ela passa o data com informações do arquivo como URL e nome do bucket, com essas informações e usando o objeto SQS do SDK é montando uma mensagem com um Json no corpo.

Essa é uma aplicação de exemplo, provavelmente com muitas falhas, dar muitas permissões como eu dei com certeza vai ser desastroso para seu balanço financeiro na AWS.

Gostaria de mais informações? tem algo de errado ou sugestões? Deixe um comentário e muito obrigado.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *