-
Notifications
You must be signed in to change notification settings - Fork 0
/
diskstore.go
150 lines (130 loc) · 3.13 KB
/
diskstore.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package diskstore
import (
"fmt"
pb "github.com/bogdanrat/web-server/contracts/proto/storage_service"
"github.com/bogdanrat/web-server/service/storage/lib"
"github.com/bogdanrat/web-server/service/storage/persistence/store"
"io"
"io/fs"
"log"
"os"
"path"
"path/filepath"
"strings"
"time"
)
type DiskStore struct {
Path string
}
func New(path string) store.Store {
return &DiskStore{
Path: path,
}
}
func (s *DiskStore) Init() error {
if err := lib.CreateDirectory(s.Path); err != nil {
return err
}
log.Printf("Initialized Disk Storage Engine in %s\n", s.Path)
return nil
}
func (s *DiskStore) Put(fileName string, body io.Reader) error {
name := filepath.Join(s.Path, fileName)
dir := filepath.Dir(name)
// create path
if err := lib.CreateDirectory(dir); err != nil {
return err
}
// create file
file, err := os.Create(name)
if err != nil {
return err
}
defer file.Close()
// copy content
_, err = io.Copy(file, body)
if err != nil {
return err
}
return nil
}
func (s *DiskStore) Get(fileName string, writer io.Writer) error {
name := filepath.Join(s.Path, fileName)
if exists, err := lib.FileExists(name); err != nil {
return err
} else if !exists {
return fmt.Errorf("file %s not found", fileName)
}
file, err := os.OpenFile(name, os.O_RDONLY, 0666)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(writer, file)
if err != nil {
return err
}
return nil
}
func (s *DiskStore) GetAll() ([]*pb.StorageObject, error) {
objects := make([]*pb.StorageObject, 0)
err := filepath.WalkDir(s.Path, func(filePath string, d fs.DirEntry, err error) error {
// avoid directories and hidden files (base == extension, e.g., .DS_Store)
if !d.IsDir() && path.Base(filePath) != filepath.Ext(filePath) {
file, err := os.Open(filePath)
if err != nil {
return err
}
fileInfo, err := file.Stat()
if err != nil {
return err
}
// remove data/ from the file name
// s.Path is ./data so we take the base of data, which is 'data'
fileName := strings.Replace(filePath, path.Base(s.Path)+"/", "", 1)
object := &pb.StorageObject{
Key: fileName,
Size: uint64(fileInfo.Size()),
LastModified: fileInfo.ModTime().Format(time.RFC3339),
}
objects = append(objects, object)
}
return nil
})
if err != nil {
return nil, err
}
return objects, nil
}
func (s *DiskStore) Delete(fileName string) error {
name := filepath.Join(s.Path, fileName)
if err := lib.TryRemoveFile(name); err != nil {
return err
}
return nil
}
func (s *DiskStore) DeleteAll(prefix ...string) error {
// if no prefix was supplied, we are going to delete every file in the storage path (i.d., ./data)
filesPath := s.Path
if len(prefix) == 1 {
filesPath = filepath.Join(s.Path, prefix[0])
}
dir, err := os.Open(filesPath)
if err != nil {
return err
}
defer dir.Close()
// get all contents of the directory associated with 'dir'
names, err := dir.Readdirnames(-1)
if err != nil {
return err
}
// remove all contents in dir
for _, name := range names {
err := os.RemoveAll(filepath.Join(filesPath, name))
if err != nil {
return err
}
}
return nil
}