forked from ego-component/eoss
-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathlocal_file.go
183 lines (162 loc) · 4.7 KB
/
local_file.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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
package eos
import (
"context"
"errors"
"fmt"
"go.uber.org/multierr"
"io"
"os"
"path"
"path/filepath"
"strings"
"sync"
)
// LocalFile is the implementation based on local files.
// For desktop APP or test.
type LocalFile struct {
// path is the content root
// all files are stored here.
path string
l sync.Mutex
// store in memory
// TODO persistent
meta map[string]map[string]string
}
func NewLocalFile(path string) (*LocalFile, error) {
err := os.MkdirAll(path, os.ModePerm)
return &LocalFile{
path: path,
meta: make(map[string]map[string]string),
}, err
}
func (l *LocalFile) GetBucketName(ctx context.Context, key string) (string, error) {
panic("implement me")
}
func (l *LocalFile) Get(ctx context.Context, key string, options ...GetOptions) (string, error) {
data, err := l.GetBytes(ctx, key, options...)
if err != nil {
return "", err
}
return string(data), err
}
func (l *LocalFile) GetBytes(ctx context.Context, key string, options ...GetOptions) ([]byte, error) {
rd, err := l.GetAsReader(ctx, key, options...)
if err != nil || rd == nil {
return nil, err
}
defer rd.Close()
return io.ReadAll(rd)
}
// GetAsReader returns reader which you need to close it.
func (l *LocalFile) GetAsReader(ctx context.Context, key string, options ...GetOptions) (io.ReadCloser, error) {
filename := l.initDir(key)
file, err := os.Open(filename)
if errors.Is(err, os.ErrNotExist) {
return nil, nil
}
return file, err
}
func (l *LocalFile) GetWithMeta(ctx context.Context, key string, attributes []string, options ...GetOptions) (io.ReadCloser, map[string]string, error) {
data, err := l.GetAsReader(ctx, key)
if err != nil {
return nil, nil, err
}
meta, err := l.Head(ctx, key, attributes)
if err != nil {
return nil, nil, err
}
return data, meta, nil
}
func (l *LocalFile) GetAndDecompress(ctx context.Context, key string) (string, error) {
return l.Get(ctx, key)
}
func (l *LocalFile) GetAndDecompressAsReader(ctx context.Context, key string) (io.ReadCloser, error) {
return l.GetAsReader(ctx, key)
}
// Put override the file
// It will create two files, one for content, one for meta.
func (l *LocalFile) Put(ctx context.Context, key string, reader io.ReadSeeker, meta map[string]string, options ...PutOptions) error {
filename := l.initDir(key)
l.l.Lock()
l.meta[key] = meta
l.l.Unlock()
f, err := os.OpenFile(filename, os.O_TRUNC|os.O_RDWR|os.O_CREATE, 0660)
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(f, reader)
return err
}
func (l *LocalFile) PutAndCompress(ctx context.Context, key string, reader io.ReadSeeker, meta map[string]string, options ...PutOptions) error {
return l.Put(ctx, key, reader, meta)
}
func (l *LocalFile) Del(ctx context.Context, key string) error {
filename := l.initDir(key)
l.l.Lock()
delete(l.meta, key)
l.l.Unlock()
return os.Remove(filename)
}
func (l *LocalFile) DelMulti(ctx context.Context, keys []string) error {
var res error
for _, key := range keys {
err := l.Del(ctx, key)
if err != nil {
err = multierr.Append(res, fmt.Errorf("faile to delete file, key %s, %w", key, err))
}
}
return res
}
func (l *LocalFile) Head(ctx context.Context, key string, attributes []string) (map[string]string, error) {
l.l.Lock()
defer l.l.Unlock()
fileMeta, ok := l.meta[key]
if !ok {
return map[string]string{}, nil
}
meta := make(map[string]string)
for _, v := range attributes {
meta[v] = fileMeta[v]
}
return meta, nil
}
func (l *LocalFile) ListObject(ctx context.Context, key string, prefix string, marker string, maxKeys int, delimiter string) ([]string, error) {
panic("implement me")
}
func (l *LocalFile) SignURL(ctx context.Context, key string, expired int64, options ...SignOptions) (string, error) {
panic("implement me")
}
func (l *LocalFile) Range(ctx context.Context, key string, offset int64, length int64) (io.ReadCloser, error) {
panic("implement me")
}
func (l *LocalFile) Exists(ctx context.Context, key string) (bool, error) {
l.l.Lock()
defer l.l.Unlock()
_, ok := l.meta[key]
return ok, nil
}
func (l *LocalFile) Copy(ctx context.Context, srcKey, dstKey string, options ...CopyOption) error {
srcPath := l.initDir(srcKey)
srcFile, err := os.OpenFile(srcPath, os.O_TRUNC|os.O_RDWR|os.O_CREATE, 0660)
if err != nil {
return err
}
dstPath := l.initDir(dstKey)
dstFile, err := os.OpenFile(dstPath, os.O_TRUNC|os.O_RDWR|os.O_CREATE, 0660)
if err != nil {
return err
}
_, err = io.Copy(dstFile, srcFile)
return err
}
// initDir returns the entire path
func (l *LocalFile) initDir(key string) string {
// compatible with Windows
segs := strings.Split(key, "/")
res := path.Join(segs...)
res = path.Join(l.path, res)
// it should never error
_ = os.MkdirAll(filepath.Dir(res), os.ModePerm)
return res
}