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)