You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

173 lines
4.1 KiB

  1. package storage
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "io/ioutil"
  8. "log"
  9. "os"
  10. "path/filepath"
  11. "time"
  12. )
  13. type LocalStorage struct {
  14. Storage
  15. basedir string
  16. logger *log.Logger
  17. cleanupJob chan struct{}
  18. }
  19. func NewLocalStorage(basedir string, cleanupInterval int, logger *log.Logger) (*LocalStorage, error) {
  20. storage := &LocalStorage{basedir: basedir, logger: logger}
  21. ticker := time.NewTicker(time.Duration(cleanupInterval) * time.Hour * 24)
  22. storage.cleanupJob = make(chan struct{})
  23. go func() {
  24. for {
  25. select {
  26. case <-ticker.C:
  27. err := storage.deleteExpired()
  28. log.Printf("error cleaning up expired files: %v", err)
  29. case <-storage.cleanupJob:
  30. ticker.Stop()
  31. return
  32. }
  33. }
  34. }()
  35. return storage, nil
  36. }
  37. func (s *LocalStorage) Type() string {
  38. return "local"
  39. }
  40. func (s *LocalStorage) Get(token string, filename string) (reader io.ReadCloser, metadata Metadata, err error) {
  41. path := filepath.Join(s.basedir, token, filename)
  42. // content type , content length
  43. reader, err = os.Open(path)
  44. if err != nil {
  45. return nil, Metadata{}, err
  46. }
  47. metadata, err = s.Head(token, filename)
  48. if err != nil {
  49. return nil, Metadata{}, err
  50. }
  51. return reader, metadata, nil
  52. }
  53. func (s *LocalStorage) Head(token string, filename string) (metadata Metadata, err error) {
  54. path := filepath.Join(s.basedir, token, fmt.Sprintf("%s.metadata", filename))
  55. fi, err := os.Open(path)
  56. if err != nil {
  57. return
  58. }
  59. err = json.NewDecoder(fi).Decode(&metadata)
  60. if err != nil {
  61. return Metadata{}, err
  62. }
  63. return metadata, nil
  64. }
  65. func (s *LocalStorage) Meta(token string, filename string, metadata Metadata) error {
  66. return s.putMetadata(token, filename, metadata)
  67. }
  68. func (s *LocalStorage) Put(token string, filename string, reader io.Reader, metadata Metadata) error {
  69. err := s.putMetadata(token, filename, metadata)
  70. if err != nil {
  71. return err
  72. }
  73. err = s.put(token, filename, reader)
  74. if err != nil {
  75. //Delete the metadata if the put failed
  76. _ = s.Delete(token, fmt.Sprintf("%s.metadata", filename))
  77. }
  78. return err
  79. }
  80. func (s *LocalStorage) Delete(token string, filename string) (err error) {
  81. dir := filepath.Join(s.basedir, token)
  82. log.Printf("deleting file %s/%s/%s", dir, token, filename)
  83. // ensure we do not accidentally delete more than the specified file in the folder
  84. if files, err := ioutil.ReadDir(dir); len(files) > 2 || err != nil {
  85. // ignore if we cannot delete the metadata file
  86. _ = os.Remove(filepath.Join(dir, fmt.Sprintf("%s.metadata", filename)))
  87. return os.Remove(filepath.Join(dir, filename))
  88. }
  89. return os.RemoveAll(dir)
  90. }
  91. func (s *LocalStorage) IsNotExist(err error) bool {
  92. if err == nil {
  93. return false
  94. }
  95. return os.IsNotExist(err)
  96. }
  97. func (s *LocalStorage) deleteExpired() error {
  98. // search for all metadata files
  99. metaFiles, err := filepath.Glob(fmt.Sprintf("%s/*/*.metadata", s.basedir))
  100. if err != nil {
  101. log.Printf("error searching for expired files %v \n", err)
  102. return err
  103. }
  104. var meta Metadata
  105. for _, file := range metaFiles {
  106. f, err := os.Open(file)
  107. if err != nil {
  108. log.Printf("error opening file: %s \n", file)
  109. return err
  110. }
  111. err = json.NewDecoder(f).Decode(&meta)
  112. if err == nil {
  113. if time.Now().After(meta.MaxDate) {
  114. // remove folder and all files in it
  115. _ = os.RemoveAll(filepath.Dir(file))
  116. }
  117. }
  118. }
  119. return nil
  120. }
  121. func (s *LocalStorage) put(token string, filename string, reader io.Reader) error {
  122. var f io.WriteCloser
  123. var err error
  124. path := filepath.Join(s.basedir, token)
  125. if err = os.MkdirAll(path, 0700); err != nil && !os.IsExist(err) {
  126. return err
  127. }
  128. if f, err = os.OpenFile(filepath.Join(path, filename), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600); err != nil {
  129. return err
  130. }
  131. defer f.Close()
  132. _, err = io.Copy(f, reader)
  133. return err
  134. }
  135. func (s *LocalStorage) putMetadata(token string, filename string, metadata Metadata) error {
  136. buffer := &bytes.Buffer{}
  137. if err := json.NewEncoder(buffer).Encode(metadata); err != nil {
  138. log.Printf("%s", err.Error())
  139. return err
  140. } else if err := s.put(token, fmt.Sprintf("%s.metadata", filename), buffer); err != nil {
  141. log.Printf("%s", err.Error())
  142. return nil
  143. }
  144. return nil
  145. }