Golang serve static files from memory

前端 未结 4 2007
忘了有多久
忘了有多久 2020-12-08 11:54

I have a quick question about serving files in Go. There is the great timesaving FileServer handler, but for my use case, I only have 2 or 3 files (js and css) that go with

4条回答
  •  佛祖请我去吃肉
    2020-12-08 12:03

    The FileServer requires a FileSystem object in its constructor. Usually, you would provide something based on http.Dir to make that FileSystem for you from the actual file system, but nothing prevents you from implementing your own:

    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 = `
        Hello world !
    
    `
    
    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)
    }
    

    This implementation is very buggy at best, and you should probably never ever use it, but it should show you how the FileSystem interface can be implemented for arbitrary 'files'.

    A more credible (and certainly less dangerous) implementation of something similar is available here. This is the one used to fake the filesystem on Go playground, so it should be a good reference (much better than mine anyway).

    Whether it is simpler to reimplement this FileSystem interface or a custom FileServer as other suggested, is entirely up to you and your project ! I suspect however that for serving a couple of predefined files, rewriting the serving part might be easier than emulating a full file-system.

提交回复
热议问题