Based on this answer, you can implement own FileSystem
for a FileServer
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'.
type InMemoryFS map[string]http.File
type InMemoryFile struct {
at int64
Name string
data []byte
fs InMemoryFS
}
func NewFile(name string, data []byte) *InMemoryFile {
return &InMemoryFile{at: 0,
Name: name,
data: data,
fs: make(InMemoryFS)}
}
// 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) {
return nil, 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 }
// CustomFsDecorator: is `http.FileSystem` decorator
type CustomFsDecorator struct {
http.FileSystem
}
func (fs CustomFsDecorator) Open(name string) (http.File, error) {
file, err := fs.FileSystem.Open(name)
if err != nil {
return nil, err
}
info, err := file.Stat()
if err != nil {
return nil, err
}
if info.IsDir() {
return file, nil
}
b, err := io.ReadAll(file)
if err != nil {
return nil, err
}
buf := new(bytes.Buffer)
// add header's lines
_, err = buf.Write([]byte("<title>My files</title>\n"))
if err != nil {
return nil, err
}
_, err = buf.Write(b)
if err != nil {
return nil, err
}
// add footer's lines
_, err = buf.Write([]byte("\n<p>And that's all, folks!</p>"))
if err != nil {
return nil, err
}
return NewFile(info.Name(), buf.Bytes()), nil
}
func Test(t *testing.T) {
cfsys := CustomFsDecorator{FileSystem: http.Dir("./static")}
fsys := http.FileServer(cfsys)
req := httptest.NewRequest(http.MethodGet, "/some.html", nil)
w := httptest.NewRecorder()
fsys.ServeHTTP(w, req)
res := w.Result()
defer func() {
_ = res.Body.Close()
}()
data, err := io.ReadAll(res.Body)
if err != nil {
t.Errorf("expected error to be nil got %v", err)
}
fmt.Println(string(data))
}
<title>My files</title>
<pre><a href="LICENSE">LICENSE</a>
<a href="README.md">README.md</a>
<a href="studio.jpg">studio.jpg</a>
</pre>
<p>And that's all, folks!</p>
PLAYGROUND