Article image
Sebastião Almeida
Sebastião Almeida05/03/2023 18:24
Compartilhe

Utilizando arquivos estáticos em executáveis Golang

  • #GoLang

Introdução

Outro dia me deparei com um problema interessante, criei um pequeno servidor web em Go que realizava a leitura de páginas html estáticas para renderizar o frontend da aplicação, porém ao compilar e executar o arquivo binário encontrei o seguinte erro:

image

Este é o meu código:

package main
import (
  "embed"
  "html/template"
  "log"
  "net/http"
)
var (
  res embed.FS
  pages = map[string]string{
      "/": "resources/index.gohtml",
  }
)
func main() {
  http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
      page := "resources/index.gohtml"
      tpl, err := template.ParseFiles(page)
      if err != nil {
          log.Printf("page %s not found in diretory...", page)
          w.WriteHeader(http.StatusInternalServerError)
          return
      }
      w.Header().Set("Content-Type", "text/html")
      w.WriteHeader(http.StatusOK)
      data := map[string]interface{}{
          "userAgent": r.UserAgent(),
      }
      if err := tpl.Execute(w, data); err != nil {
          return
      }
  })
  http.FileServer(http.FS(res))
  log.Println("server started...")
  err := http.ListenAndServe(":8080", nil)
  if err != nil {
      panic(err)
  }
}

Curiosamente quando testei o servidor através do comando go run ele funcionou normalmente, porém após usar o comando go build para compilar o código e iniciar o programa através do executável gerado ele não encontrava mais os arquivos estáticos, era como se eles estivessem desaparecido.

Isso ocorre porque os arquivos estáticos como imagens, folhas de estilo, arquivos JavaScript e etc não são incorporados ao binário gerado durante a compilação e por isso o programa não consegue encontrá-los. Para o binário conseguir encontrar esses arquivos eles precisam ser incorporados em tempo de compilação.

Resolvendo com embed

Para compilar arquivos estáticos em um executável em Go, você pode usar o pacote “embed” introduzido no Go 1.16.

O pacote “embed” permite que você incorpore arquivos estáticos, como imagens, folhas de estilo e arquivos JavaScript, diretamente em seu código Go. O Go os incluirá no executável compilado, permitindo que você os acesse diretamente do código sem precisar distribuir os arquivos separadamente.

Um simples exemplo

Aqui está um exemplo de como usar o pacote “embed” para incorporar arquivos estáticos em seu código Go e depois compilá-los em um executável.

package main

import (
 "embed"
 "io/fs"
 "net/http"
)

//go:embed static/*
var staticFiles embed.FS

func main() {
 // Cria um sistema de arquivos a partir dos arquivos incorporados.
 fileServer := http.FileServer(http.FS(staticFiles))

 // Registra o sistema de arquivos como manipulador HTTP.
 http.Handle("/", fileServer)

 // Inicia o servidor HTTP na porta 8080
 http.ListenAndServe(":8080", nil)
}

Neste exemplo, o pacote “embed” é usado para incorporar todos os arquivos no diretório “static” em um sistema de arquivos virtual “staticFiles”. Em seguida, o sistema de arquivos é registrado como um manipulador HTTP usando o pacote “http.FileServer”.

Finalmente, o servidor HTTP é iniciado na porta 8080 usando o método “http.ListenAndServe”. Quando o programa é compilado, os arquivos no diretório “static” são incorporados diretamente no executável.

Aplicando ao nosso código

package main
import (
  "embed"
  "html/template"
  "log"
  "net/http"
)
var (
  //go:embed resources/index.gohtml
  res embed.FS
  pages = map[string]string{
      "/": "resources/index.gohtml",
  }
)
func main() {
  http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
      page, ok := pages[r.URL.Path]
      if !ok {
          w.WriteHeader(http.StatusNotFound)
          return
      }
      tpl, err := template.ParseFS(res, page)
      if err != nil {
          log.Printf("page %s not found in pages cache...", r.RequestURI)
          w.WriteHeader(http.StatusInternalServerError)
          return
      }
      w.Header().Set("Content-Type", "text/html")
      w.WriteHeader(http.StatusOK)
      data := map[string]interface{}{
          "userAgent": r.UserAgent(),
      }
      if err := tpl.Execute(w, data); err != nil {
          return
      }
  })
  http.FileServer(http.FS(res))
  log.Println("server started...")
  err := http.ListenAndServe(":8080", nil)
  if err != nil {
      panic(err)
  }
}

Utilizamos o “embed” para incorporar o arquivo “index.gohtml” a variável “res” em tempo de compilação, dessa forma a função “ParseFS” não vai estar mapeando uma variável não inicializada, mas sim uma variável com o conteúdo de “index.gohtml”.

Considerações finais

É importante notar que o pacote “embed” é compatível apenas com o Go 1.16 ou posterior. Se você estiver usando uma versão anterior do Go, precisará usar outra técnica para incorporar arquivos estáticos em seu código, como embutir os arquivos usando um utilitário externo durante a compilação ou usar um pacote de terceiros. Até a próxima.

Compartilhe
Comentários (0)