Manipular arquivos em container com Golang
🚀 Enviando Arquivos para o S3 com Go: Dinâmico em Memória vs. Arquivo no Filesystem
Durante um experimento recente, explorei duas formas diferentes de gerar e enviar arquivos JSON para um bucket S3 usando uma aplicação escrita em Go, empacotada em Docker. O objetivo era simples: entender as vantagens e implicações de trabalhar com dados em memória versus arquivos físicos no container.
💡 Objetivo
- Criar um endpoint HTTP (/envia) que, quando acessado:
- Gera um arquivo JSON com dados simulados (nome, idade);
- Envia esse arquivo para um bucket S3 da AWS;
🧪 Primeira abordagem: geração dinâmica em memória
A primeira implementação construiu o conteúdo JSON diretamente na memória, utilizando um strings.NewReader() para enviá-lo ao S3 sem criar um arquivo físico.
data := `{"nome": "Renato", "idade": 35}`
_, err = s3Client.PutObject(ctx, &s3.PutObjectInput{
Bucket: &bucket,
Key: pointer("arquivo-temporario.json"),
Body: strings.NewReader(data),
})
Vantagens:
Não escreve no disco;
Mais performático para payloads pequenos;
Código mais enxuto.
Desvantagens:
Menos flexível se quisermos reutilizar o arquivo, validar ou logar em disco;
Para arquivos grandes, consome mais memória RAM.
🧪 Segunda abordagem: geração no filesystem (/tmp)
Na segunda versão, escolhi salvar o conteúdo JSON fisicamente no container, em /tmp/arquivo-temporario.json, e somente depois fazer a leitura desse arquivo para upload.
err := os.WriteFile("/tmp/arquivo-temporario.json", []byte(data), 0644)
file, err := os.Open("/tmp/arquivo-temporario.json")
_, err = s3Client.PutObject(ctx, &s3.PutObjectInput{
Bucket: &bucket,
Key: pointer("arquivo-temporario.json"),
Body: file,
})
Vantagens:
Arquivo pode ser validado antes do envio;
Facilita testes, logs e debug;
Ideal para cenários em que o arquivo é gerado por outra ferramenta (ex: conversão de imagem, relatório, etc.).
Desvantagens:
Maior uso de disco;
Exige mais manipulação de arquivos (criar, abrir, fechar, remover).
🐳 Empacotando tudo com Docker
A aplicação foi empacotada com um Dockerfile multi-stage, utilizando a imagem distroless para segurança e leveza. O binário Go foi compilado estaticamente (CGO_ENABLED=0), o que garantiu compatibilidade e independência de bibliotecas do sistema, como glibc.
🔥 Conclusão
Esses dois métodos atendem a diferentes casos de uso:
- Para dados pequenos e envio rápido: use memória.
- Para arquivos grandes ou reutilizáveis: salve no disco.
- Ambos são válidos — e o mais importante é entender o impacto de cada escolha em termos de performance, segurança e controle.
Se quiser o código completo, segue o github do repositório com tudo configurado: main.go, Dockerfile, .gitignore, README.md e mais.