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.