Golang从内存中提供静态文件

Fer*_*ras 17 static assets http go

我有一个关于在Go中提供文件的快速问题.有很大的省时FileServer处理程序,但对于我的用例,我只有2或3个文件(js和css)与我的应用程序一起使用,我不想让部署复杂化,不得不考虑这些.

你认为有一种简单的方法可以将这些文件构建到二进制文件中并从那里提供它们.例如,base64将文件的数据编码为常量,并从常量中为文件提供服务.这将以最简单的形式工作,但我不想经历我自己做文件服务器所做的一切(标题,expiries,mime-types等)的痛苦.那么有一种简单的方法可以将这些静态文件以某种形式烘焙到二进制文件中并以这种方式提供它们吗?

val*_*val 19

FileServer要求FileSystem在其构造的对象.通常情况下,你将提供基于什么http.Dir来做出FileSystem你从实际的文件系统,但没有阻止你实现自己的:

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)
}
Run Code Online (Sandbox Code Playgroud)

这个实现是非常马车在最好的,你应该永远使用它,但它应该告诉你如何将FileSystem接口可以任意"文件"来实现.

一个更可信的(当然不那么危险)实施的类似的东西,请点击这里.这是用来假冒 Go操场上的文件系统的那个,所以它应该是一个很好的参考(比我的好多了).

是否更简单地重新实现此FileSystem接口或FileServer其他建议的自定义,完全取决于您和您的项目!但我怀疑,为了提供几个预定义文件,重写服务部分可能比模拟完整的文件系统更容易.


Gre*_*reg 8

" go.rice "包为您解决了这个问题 - 在二进制文件中嵌入资源,并提供http.FileSystem实现.


ANi*_*sus 5

做你要求的事情并不是很困难.你不必对它进行base64编码或任何事情(它只会让你更难编辑.).

下面是如何输出具有正确mime类型的javascript文件的示例:

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)
}
Run Code Online (Sandbox Code Playgroud)

  • 但是,如果您不使用文件系统,则不能将其留给文件服务器来处理mime类型. (3认同)