embedfs.go 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. package util
  2. import (
  3. "embed"
  4. "errors"
  5. "io"
  6. "io/fs"
  7. "time"
  8. )
  9. // CachingEmbedFS is a wrapper around embed.FS that allows setting a ModTime, so that the
  10. // default static file server can send 304s back. It can be used like this:
  11. //
  12. // var (
  13. // //go:embed docs
  14. // docsStaticFs embed.FS
  15. // docsStaticCached = &util.CachingEmbedFS{ModTime: time.Now(), FS: docsStaticFs}
  16. // )
  17. //
  18. // http.FileServer(http.FS(docsStaticCached)).ServeHTTP(w, r)
  19. type CachingEmbedFS struct {
  20. ModTime time.Time
  21. FS embed.FS
  22. }
  23. // Open opens a file in the embedded filesystem and returns a fs.File with the static ModTime
  24. func (f CachingEmbedFS) Open(name string) (fs.File, error) {
  25. file, err := f.FS.Open(name)
  26. if err != nil {
  27. return nil, err
  28. }
  29. stat, err := file.Stat()
  30. if err != nil {
  31. return nil, err
  32. }
  33. return &cachingEmbedFile{file, f.ModTime, stat}, nil
  34. }
  35. type cachingEmbedFile struct {
  36. file fs.File
  37. modTime time.Time
  38. fs.FileInfo
  39. }
  40. func (f cachingEmbedFile) Stat() (fs.FileInfo, error) {
  41. return f, nil
  42. }
  43. func (f cachingEmbedFile) Read(bytes []byte) (int, error) {
  44. return f.file.Read(bytes)
  45. }
  46. func (f *cachingEmbedFile) Seek(offset int64, whence int) (int64, error) {
  47. if seeker, ok := f.file.(io.Seeker); ok {
  48. return seeker.Seek(offset, whence)
  49. }
  50. return 0, errors.New("io.Seeker not implemented")
  51. }
  52. func (f cachingEmbedFile) ModTime() time.Time {
  53. return f.modTime // We override this!
  54. }
  55. func (f cachingEmbedFile) Close() error {
  56. return f.file.Close()
  57. }