<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Filippo Ippolito</title>
    <description>The latest articles on Forem by Filippo Ippolito (@filippoipp).</description>
    <link>https://forem.com/filippoipp</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F446961%2Fc996b07b-efb1-42f1-9d8c-eac8730cdf3a.jpeg</url>
      <title>Forem: Filippo Ippolito</title>
      <link>https://forem.com/filippoipp</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/filippoipp"/>
    <language>en</language>
    <item>
      <title>Redimensionamento e upload de imagens em um bucket do Amazon S3 utilizando GoLang.</title>
      <dc:creator>Filippo Ippolito</dc:creator>
      <pubDate>Wed, 05 Aug 2020 02:09:13 +0000</pubDate>
      <link>https://forem.com/filippoipp/redimensionamento-e-upload-de-imagens-em-um-bucket-do-amazon-s3-utilizando-golang-m36</link>
      <guid>https://forem.com/filippoipp/redimensionamento-e-upload-de-imagens-em-um-bucket-do-amazon-s3-utilizando-golang-m36</guid>
      <description>&lt;p&gt;Recentemente uma demanda, inicialmente desafiadora, apareceu em uma sprint e fiquei encarregado de resolve-lá. Criar um microserviço capaz de redimensionar uma imagem em três tamanhos diferentes e posteriormente realizar o upload das mesmas juntamente com a imagem original em um bucket do Amazon S3.&lt;/p&gt;

&lt;p&gt;Anteriormente esse serviço era realizado na nossa própria API desenvolvida em node, o que nos gerou alguns problemas de desempenho e a necessidade de mudança.&lt;/p&gt;

&lt;p&gt;Nosso CTO sugeriu a linguagem GoLang, visto que é uma linguagem que possuí duas características fascinantes:&lt;br&gt;
facilidade em programar e sua alta capacidade de performance, diferentemente de linguagens como C e C++ que possuem alta performance porém alta complexidade de desenvolvimento. Após algumas pesquisas resolvi aceitar e desafio e começar a programar.&lt;/p&gt;

&lt;p&gt;Após configurar o ambiente de desenvolvimento (utilizei o GoLand da JetBrains), comecei por importar os pacotes que iria utilizar:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/disintegration/imaging"
"github.com/julienschmidt/httprouter"
"github.com/nfnt/resize" #
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Definindo os pacotes que precisaria, criei uma função que recebendo alguns parâmetros específicos para uma requisição http:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func handler(w http.ResponseWriter, r *http.Request, ps httprouter.Params)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Com a declaração da função pronta, começamos recebendo a imagem através de um Multipart.File (upload) e a acessamos através de um FormFile de key "file":&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;file, fileHeader, err := r.FormFile("file")
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Feito isso podemos criar uma sessão na AWS utilizando um pacote importado anteriormente:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;s, err := session.NewSession(&amp;amp;aws.Config{
    Region: aws.String("região-aws"),
    Credentials: credentials.NewStaticCredentials(
        "id-secreto-aws", // id
        "key-secreta-aws",   // secret
        ""),  // token podemos deixar em branco
})
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Agora defino 2 arrays com os tamanhos que quero que as imagens sejam redimensionadas (utilizo o tipo uint pois é o que a função de redimensionamento do pacote aceita) e também transformo meu file do tipo file em tipo imagem.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt; //defino tamanhos de redimensionamento da imagem
 width := [3]uint{1140, 770, 350}
 height := [3]uint{550, 650, 250}

 //file para formato jpg
 img, err := imaging.Decode(file)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Após definir os tamanhos de redimensionamento e transformar o arquivo em imagem, faço um loop para redimensionar a imagem em cada tamanho definido e chamar outra função para realizar o upload no bucket do Amazon S3, e essa funçao receberá como parâmetros a sessão da aws criada anteriormente, a imagem redimensionada e fileHeader do file original recebido:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for i:= 0; i &amp;lt; 3; i++ {
    image := resize.Resize(width[i], height[i], img, resize.Lanczos3)

    fileName, err := UploadFileToS3(s, image, fileHeader)
    if err != nil {
        fmt.Fprintf(w, "Could not upload file")
    }

    fmt.Fprintln(w, "Image uploaded successfully: ", fileName)

}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Vou chamar a minha função de upload na amazon de UploadFileToS3 e ela retornará uma string com o nome do arquivo criado ou um erro (note que o retorno da função é armazenado em uma variável fileName no passo anterior):&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt; func UploadFileToS3(s *session.Session, image image.Image, fileHeader *multipart.FileHeader) (string, error)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Enfim chegou a hora de fazer o upload das fotos redimensionadas. Aqui o primeiro passo é coletar os dados codificados na memória e salvar em uma variável e apenas depois disso realizar a codificação. O segundo passo é criar um nome único para o arquivo que servirá como uma key (buscar, remover arquivos) e o terceiro passo é configurar o upload para o aws S3:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// get the file size and read
// the file content into a buffer
buf := new(bytes.Buffer)
err := jpeg.Encode(buf, image, nil)
if err != nil {
    fmt.Println("failed to create buffer", err)
}

// create a unique file name for the file
tempFileName := "" + bson.NewObjectId().Hex() +                      filepath.Ext(fileHeader.Filename)

// config settings: this is where you choose the bucket,
// filename, content-type and storage class of the file
// you're uploading
_, err = s3.New(s).PutObject(&amp;amp;s3.PutObjectInput{
    Bucket:               aws.String("nome-do-seu-bucket-no-s3"),
    Key:                  aws.String(tempFileName),
    ACL:                  aws.String("public-read"),// pode ser privado
    Body:                 bytes.NewReader(buf.Bytes()),
    ContentType:        aws.String(http.DetectContentType(buf.Bytes())),
})
if err != nil {
    return "", err
}

return tempFileName, err
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Estamos quase lá! Nossas funções de redimensionamento e upload já estão prontas e agora só precisamos chama-las no nosso main. Como posteriormente terei que utilizar parâmetros na minha rota, tive que usar uma biblioteca específica para instanciar um novo router, definir a minha rota e informar em qual porta meu servidor estará rodando:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt; func main() {
   router:= httprouter.New()
   router.POST("/upload/:id", handler)
   log.Println("Upload server started")
   log.Fatal(http.ListenAndServe(":3000", router))
 }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;E prontinho... realizamos nosso serviço de redimensionamento e upload de imagens.&lt;br&gt;
Ainda realizarei algumas adições no sistema mas a base está pronta e creio que nada mal para um primeiro contato com a linguagem.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>go</category>
    </item>
  </channel>
</rss>
