embedfs.go 1.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  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. //
  20. type CachingEmbedFS struct {
  21. ModTime time.Time
  22. FS embed.FS
  23. }
  24. // Open opens a file in the embedded filesystem and returns a fs.File with the static ModTime
  25. func (f CachingEmbedFS) Open(name string) (fs.File, error) {
  26. file, err := f.FS.Open(name)
  27. if err != nil {
  28. return nil, err
  29. }
  30. stat, err := file.Stat()
  31. if err != nil {
  32. return nil, err
  33. }
  34. return &cachingEmbedFile{file, f.ModTime, stat}, nil
  35. }
  36. type cachingEmbedFile struct {
  37. file fs.File
  38. modTime time.Time
  39. fs.FileInfo
  40. }
  41. func (f cachingEmbedFile) Stat() (fs.FileInfo, error) {
  42. return f, nil
  43. }
  44. func (f cachingEmbedFile) Read(bytes []byte) (int, error) {
  45. return f.file.Read(bytes)
  46. }
  47. func (f *cachingEmbedFile) Seek(offset int64, whence int) (int64, error) {
  48. if seeker, ok := f.file.(io.Seeker); ok {
  49. return seeker.Seek(offset, whence)
  50. }
  51. return 0, errors.New("io.Seeker not implemented")
  52. }
  53. func (f cachingEmbedFile) ModTime() time.Time {
  54. return f.modTime // We override this!
  55. }
  56. func (f cachingEmbedFile) Close() error {
  57. return f.file.Close()
  58. }