http static go assets

http - Golang sirve archivos estáticos de memoria



static assets (4)

No es muy difícil hacer lo que solicitas. No es necesario que base64 lo codifique ni nada (solo hará que sea más difícil editarlo).

A continuación se muestra un ejemplo de cómo generar un archivo javascript con el tipo de mime correcto:

package main import ( "fmt" "log" "net/http" ) const jsFile = `alert(''Hello World!'');` func main() { http.HandleFunc("/file.js", JsHandler) log.Fatal(http.ListenAndServe(":8080", nil)) } func JsHandler(w http.ResponseWriter, r *http.Request) { // Getting the headers so we can set the correct mime type headers := w.Header() headers["Content-Type"] = []string{"application/javascript"} fmt.Fprint(w, jsFile) }

Tengo una pregunta rápida sobre la publicación de archivos en Go. Existe el gran controlador FileServer que ahorra tiempo, pero para mi caso de uso, solo tengo 2 o 3 archivos (js y css) que van con mi aplicación y no quiero complicar el despliegue para tener que pensar en ellos.

¿Crees que hay una manera fácil de construir esos pares de archivos en el binario y servirlos desde allí. Por ejemplo, base64 codifica los datos de los archivos como constantes y servidores de los archivos de las constantes. Esto funcionaría en su forma más simple, pero no quiero pasar por el dolor de hacer todo lo que hace un servidor de archivos (encabezados, vencimientos, tipos de mime, etc.) por mi cuenta. Entonces, ¿habría una manera fácil de convertir esos archivos estáticos en el binario de alguna forma y servirlos de esa manera?


Yo almacenaría los archivos en variable como texto sin formato. Algo como esto:

package main import ( "fmt" "log" "net/http" ) var files = map[string]string{} func init() { files["style.css"] = ` /* css file content */ body { background-color: pink; } ` } func init() { files["index.html"] = ` <!-- Html content --> <html><head> <link rel="stylesheet" type="text/css" href="style.css"> </head><body>Hello world!</body></html> ` } func main() { for fileName, content := range files { contentCpy := content http.HandleFunc("/"+fileName, func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "%s/n", contentCpy) }) } log.Fatal(http.ListenAndServe(":8080", nil)) }

De esta forma, es bastante fácil tener tu archivo MAKE o tu script de compilación, algo así como:

for file in index.html style.css; do echo "package main/nfunc init() { files[/"$file/"] = /`$(cat $file)/` }" | gofmt -s > $file.go; done; go build && ./httptest


El FileServer requiere un objeto FileSystem en su constructor. Por lo general, debe proporcionar algo basado en http.Dir para que FileSystem lo proporcione desde el sistema de archivos real, pero nada le impide implementar el suyo propio:

package main import "os" import "time" import "net/http" type InMemoryFS map[string]http.File // Implements FileSystem interface func (fs InMemoryFS) Open(name string) (http.File, error) { if f, ok := fs[name]; ok { return f, nil } panic("No file") } type InMemoryFile struct { at int64 Name string data []byte fs InMemoryFS } func LoadFile(name string, val string, fs InMemoryFS) *InMemoryFile { return &InMemoryFile{at: 0, Name: name, data: []byte(val), fs: fs} } // Implements the http.File interface func (f *InMemoryFile) Close() error { return nil } func (f *InMemoryFile) Stat() (os.FileInfo, error) { return &InMemoryFileInfo{f}, nil } func (f *InMemoryFile) Readdir(count int) ([]os.FileInfo, error) { res := make([]os.FileInfo, len(f.fs)) i := 0 for _, file := range f.fs { res[i], _ = file.Stat() i++ } return res, nil } func (f *InMemoryFile) Read(b []byte) (int, error) { i := 0 for f.at < int64(len(f.data)) && i < len(b) { b[i] = f.data[f.at] i++ f.at++ } return i, nil } func (f *InMemoryFile) Seek(offset int64, whence int) (int64, error) { switch whence { case 0: f.at = offset case 1: f.at += offset case 2: f.at = int64(len(f.data)) + offset } return f.at, nil } type InMemoryFileInfo struct { file *InMemoryFile } // Implements os.FileInfo func (s *InMemoryFileInfo) Name() string { return s.file.Name } func (s *InMemoryFileInfo) Size() int64 { return int64(len(s.file.data)) } func (s *InMemoryFileInfo) Mode() os.FileMode { return os.ModeTemporary } func (s *InMemoryFileInfo) ModTime() time.Time { return time.Time{} } func (s *InMemoryFileInfo) IsDir() bool { return false } func (s *InMemoryFileInfo) Sys() interface{} { return nil } const HTML = `<html> Hello world ! </html> ` const CSS = ` p { color:red; text-align:center; } ` func main() { FS := make(InMemoryFS) FS["foo.html"] = LoadFile("foo.html", HTML, FS) FS["bar.css"] = LoadFile("bar.css", CSS, FS) http.Handle("/", http.FileServer(FS)) http.ListenAndServe(":8080", nil) }

Esta implementación es muy defectuosa en el mejor de los casos, y probablemente nunca deberías usarla, pero debería mostrarte cómo se puede implementar la interfaz FileSystem para ''archivos'' arbitrarios.

Una implementación más creíble (y ciertamente menos peligrosa) de algo similar está disponible aquí . Este es el que se utiliza para falsificar el sistema de archivos en Go playground, por lo que debe ser una buena referencia (mucho mejor que la mía de todos modos).

Si es más sencillo volver a implementar esta interfaz FileSystem o un FileServer personalizado como otros sugirieron, ¡es totalmente de usted y de su proyecto! Sin embargo, sospecho que para servir un par de archivos predefinidos, reescribir la parte de servicio podría ser más fácil que emular un sistema de archivos completo.


El paquete " go.rice " se ocupa de esto por usted, incorporando recursos en sus binarios y proporcionando una implementación de http.FileSystem.