DEV Community

Cover image for Redimensionamento e upload de imagens em um bucket do Amazon S3 utilizando GoLang.
Filippo Ippolito
Filippo Ippolito

Posted on

Redimensionamento e upload de imagens em um bucket do Amazon S3 utilizando GoLang.

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.

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.

Nosso CTO sugeriu a linguagem GoLang, visto que é uma linguagem que possuí duas características fascinantes:
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.

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

"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" #

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

func handler(w http.ResponseWriter, r *http.Request, ps httprouter.Params)

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":

file, fileHeader, err := r.FormFile("file")

Feito isso podemos criar uma sessão na AWS utilizando um pacote importado anteriormente:

s, err := session.NewSession(&aws.Config{
    Region: aws.String("região-aws"),
    Credentials: credentials.NewStaticCredentials(
        "id-secreto-aws", // id
        "key-secreta-aws",   // secret
        ""),  // token podemos deixar em branco
})

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.

 //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)

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:

for i:= 0; i < 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)

}

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):

 func UploadFileToS3(s *session.Session, image image.Image, fileHeader *multipart.FileHeader) (string, error)

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:

// 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(&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

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:

 func main() {
   router:= httprouter.New()
   router.POST("/upload/:id", handler)
   log.Println("Upload server started")
   log.Fatal(http.ListenAndServe(":3000", router))
 }

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

Top comments (0)