[英]Golang serve static files from memory
I have a quick question about serving files in Go. 我有一个关于在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 my app and I dont want to complicate the deployment to have to think about those.
有很大的省时FileServer处理程序,但对于我的用例,我只有2或3个文件(js和css)与我的应用程序一起使用,我不想让部署复杂化,不得不考虑这些。
Do you think there is an easy way to build those couple of files into the binary and serve them from there. 你认为有一种简单的方法可以将这些文件构建到二进制文件中并从那里提供它们。 For example base64 encode the data of the files as constants and server the files from the constants.
例如,base64将文件的数据编码为常量,并从常量中为文件提供服务。 This would work in its most simple form, but I dont want to go through the pain of doing everything that a file server does (headers, expiries, mime-types, etc) on my own.
这将以最简单的形式工作,但我不想经历我自己做文件服务器所做的一切(标题,expiries,mime-types等)的痛苦。 So would there be an easy way to bake those static files into the binary in some form and serve them that way?
那么有一种简单的方法可以将这些静态文件以某种形式烘焙到二进制文件中并以这种方式提供它们吗?
The FileServer
requires a FileSystem
object in its constructor. FileServer
在其构造函数中需要FileSystem
对象。 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: 通常情况下,你将提供基于什么
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)
}
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'. 这个实现是非常错误的最好的,你应该永远使用它,但它应该告诉你如何将
FileSystem
接口可以任意“文件”来实现。
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).
这是用于在Go操场上伪造文件系统的那个 ,所以它应该是一个很好的参考(比我的好多了)。
Whether it is simpler to reimplement this FileSystem
interface or a custom FileServer
as other suggested, is entirely up to you and your project ! 是否更容易重新实现此
FileSystem
接口或其他建议的自定义FileServer
,完全取决于您和您的项目! I suspect however that for serving a couple of predefined files, rewriting the serving part might be easier than emulating a full file-system. 但我怀疑,为了提供几个预定义文件,重写服务部分可能比模拟完整的文件系统更容易。
“ go.rice ”包为您解决了这个问题 - 在二进制文件中嵌入资源,并提供http.FileSystem实现。
It is not very difficult to do what you request. 做你要求的事情并不是很困难。 You don't have to base64 encode it or anything (it will just make it harder for you to edit.).
你不必对它进行base64编码或任何事情(它只会让你更难编辑。)。
Below is an example of how to output a javascript file with correct mime type: 下面是如何输出具有正确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)
}
I would store the files in variable as plain text. 我将文件存储在变量中作为纯文本。 Something like this:
像这样的东西:
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))
}
That way, it is pretty easy to have your makefile or build script so something like: 这样,你可以很容易地使用makefile或build脚本:
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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.