Biblioteca afero para Go: abstração de sistema de arquivos
A biblioteca afero (pronuncia-se "aféro") é uma abstração de sistema de arquivos poderosa e flexível para a linguagem Go. Ela fornece uma interface unificada para trabalhar com diferentes tipos de sistemas de arquivos: o sistema de arquivos real do sistema operacional, um sistema de arquivos virtual em memória (in-memory), um sistema de arquivos baseado em strings, além de permitir criar implementações personalizadas com facilidade. Isso torna a afero uma ferramenta indispensável para testes, escrita de código portável e criação de ambientes de execução isolados.
Por que usar a afero? Na biblioteca padrão do Go, a manipulação de arquivos está fortemente vinculada ao sistema de arquivos real através dos pacotes os e io/ioutil. Isso gera problemas ao escrever testes: testes que trabalham com arquivos reais são lentos, não determinísticos e exigem limpeza após a execução. A afero resolve esse problema permitindo substituir o sistema de arquivos real por um virtual (por exemplo, in-memory) nos testes. Além disso, a afero simplifica a escrita de código que deve funcionar da mesma forma em diferentes plataformas (Windows, Linux, macOS), abstraindo diferenças em caminhos e permissões de acesso.
A biblioteca oferece um rico conjunto de funções, totalmente compatíveis com as interfaces io.Reader, io.Writer, io.Closer e outras. Ela suporta todas as operações principais: criação, leitura, escrita, exclusão de arquivos e diretórios, além de trabalhar com permissões de acesso e links simbólicos. A afero é amplamente utilizada em projetos onde a testabilidade e a portabilidade são importantes, como Hugo (gerador de sites estáticos) e Cobra (framework para aplicações CLI).
Instalação
Para instalar a biblioteca afero, execute o seguinte comando no terminal na raiz do seu projeto Go:
go get github.com/spf13/afero
Após a execução do comando, a biblioteca será adicionada ao seu arquivo go.mod e estará pronta para uso. Certifique-se de ter o Go versão 1.16 ou superior instalado.
Início rápido
Abaixo está um exemplo mínimo funcional que demonstra a criação de um arquivo, escrita e leitura usando um sistema de arquivos in-memory:
package main
import (
"fmt"
"github.com/spf13/afero"
)
func main() {
// Cria um sistema de arquivos virtual em memória
fs := afero.NewMemMapFs()
// Cria um arquivo
file, _ := fs.Create("/hello.txt")
defer file.Close()
// Escreve dados
file.WriteString("Olá, afero!")
// Lê o arquivo
data, _ := afero.ReadFile(fs, "/hello.txt")
fmt.Println(string(data)) // Saída: Olá, afero!
}
Neste exemplo, criamos um sistema de arquivos virtual MemMapFs, que fica completamente na memória RAM. Em seguida, criamos um arquivo, escrevemos uma string e a lemos de volta. Todo o código funciona sem acessar o disco real, o que é ideal para testes.
Métodos e funções principais
A biblioteca afero oferece diversos métodos para manipular o sistema de arquivos. Veja 8 dos principais.
1. Create(name string) (File, error)
Assinatura: Create(name string) (afero.File, error)
O que faz: Cria um novo arquivo com o nome especificado ou abre um existente para escrita (com truncamento). Retorna um objeto File que implementa as interfaces io.ReadWriteCloser e io.Seeker.
fs := afero.NewMemMapFs()
file, err := fs.Create("/novoarquivo.txt")
if err != nil {
fmt.Println("Erro ao criar arquivo:", err)
return
}
defer file.Close()
file.WriteString("Dados para o novo arquivo")
2. Open(name string) (File, error)
Assinatura: Open(name string) (afero.File, error)
O que faz: Abre um arquivo existente para leitura. Retorna um objeto File que implementa io.ReadCloser.
fs := afero.NewMemMapFs()
// ... (criação do arquivo omitida)
file, err := fs.Open("/novoarquivo.txt")
if err != nil {
fmt.Println("Erro ao abrir arquivo:", err)
return
}
defer file.Close()
data := make([]byte, 100)
n, _ := file.Read(data)
fmt.Println(string(data[:n]))
3. OpenFile(name string, flag int, perm os.FileMode) (File, error)
Assinatura: OpenFile(name string, flag int, perm os.FileMode) (afero.File, error)
O que faz: Abre um arquivo com opções avançadas, como flags (O_RDONLY, O_WRONLY, O_RDWR, O_CREATE, O_APPEND, etc.) e permissões específicas. É a versão mais flexível para abrir arquivos.
fs := afero.NewMemMapFs()
// Abre ou cria o arquivo para leitura e escrita, com permissão 0644
file, err := fs.OpenFile("/dados.txt", os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
fmt.Println("Erro:", err)
return
}
defer file.Close()
file.WriteString("Anexando dados")
4. Mkdir(name string, perm os.FileMode) error
Assinatura: Mkdir(name string, perm os.FileMode) error
O que faz: Cria um novo diretório com o nome e permissões especificados. Retorna um erro se o diretório já existir ou se o caminho for inválido.
fs := afero.NewMemMapFs()
err := fs.Mkdir("/meudiretorio", 0755)
if err != nil {
fmt.Println("Erro ao criar diretório:", err)
}
5. MkdirAll(path string, perm os.FileMode) error
Assinatura: MkdirAll(path string, perm os.FileMode) error
O que faz: Cria um diretório e todos os diretórios pais necessários (similar ao mkdir -p do Linux). Não retorna erro se o diretório já existir.
fs := afero.NewMemMapFs()
err := fs.MkdirAll("/pasta/subpasta/outra", 0755)
if err != nil {
fmt.Println("Erro:", err)
}
6. Remove(name string) error
Assinatura: Remove(name string) error
O que faz: Remove um arquivo ou diretório vazio. Retorna um erro se o arquivo não existir ou se o diretório não estiver vazio.
fs := afero.NewMemMapFs()
fs.Create("/temp.txt")
err := fs.Remove("/temp.txt")
if err != nil {
fmt.Println("Erro ao remover:", err)
}
7. RemoveAll(path string) error
Assinatura: RemoveAll(path string) error
O que faz: Remove um arquivo ou diretório e todo o seu conteúdo recursivamente (similar ao rm -rf).
fs := afero.NewMemMapFs()
fs.MkdirAll("/pasta/subpasta", 0755)
err := fs.RemoveAll("/pasta")
if err != nil {
fmt.Println("Erro:", err)
}
8. Rename(oldname, newname string) error
Assinatura: Rename(oldname, newname string) error
O que faz: Renomeia (move) um arquivo ou diretório de um caminho para outro. Funciona apenas dentro do mesmo sistema de arquivos.
fs := afero.NewMemMapFs()
fs.Create("/velho.txt")
err := fs.Rename("/velho.txt", "/novo.txt")
if err != nil {
fmt.Println("Erro ao renomear:", err)
}
Conclusão
A biblioteca afero é uma solução robusta e versátil para abstração de sistemas de arquivos em Go. Seja para testes unitários, simulação de ambientes ou criação de código portável, ela oferece uma API consistente e de alto desempenho. Comece a usar afero hoje mesmo e simplifique sua manipulação de arquivos!