-
Notifications
You must be signed in to change notification settings - Fork 0
/
file.go
337 lines (315 loc) · 9.41 KB
/
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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
package file
import (
"bufio"
"errors"
"github.com/api0-work/plugin"
"github.com/ssgo/u"
"os"
"strings"
"sync"
)
var allowPaths = make([]string, 0)
var allowExtensions = make([]string, 0)
var notAllowMessage = ""
var fileConfigLock = sync.RWMutex{}
var lockFile = func(f *os.File) {}
var unlockFile = func(f *os.File) {}
type File struct {
name string
fd *os.File
}
func init() {
plugin.Register(plugin.Plugin{
Id: "github.com/api0-work/plugins/file",
Name: "file",
ConfigSet: []plugin.ConfigSet{
{Name: "allowPaths", Type: "[]string", Memo: "允许操作的文件路径"},
{Name: "allowExtensions", Type: "[]string", Memo: "允许操作的文件后缀,以.开头,例如 .json .txt .db"},
{Name: "notAllowMessage", Type: "string", Memo: "当文件路径或文件后缀不被允许时返回的错误信息"},
},
Init: func(conf map[string]interface{}) {
newAllowPaths := make([]string, 0)
newAllowExtensions := make([]string, 0)
newNotAllowMessage := "file not allow to access"
if conf["allowPaths"] != nil {
u.Convert(conf["allowPaths"], &newAllowPaths)
}
if conf["allowExtensions"] != nil {
u.Convert(conf["allowExtensions"], &newAllowExtensions)
}
if conf["notAllowMessage"] != nil {
newNotAllowMessage = u.String(conf["notAllowMessage"])
}
fileConfigLock.Lock()
allowPaths = newAllowPaths
allowExtensions = newAllowExtensions
notAllowMessage = newNotAllowMessage
fileConfigLock.Unlock()
},
Objects: map[string]interface{}{
// read 读取一个文件
// * fileName 文件名
// read return 文件内容,字符串格式
"read": func(fileName string) (string, error) {
if f, err := openFileForRead(fileName); err == nil {
defer f.Close()
return f.ReadAll()
}else{
return "", err
}
},
// readBytes 读取一个二进制文件
// readBytes return 文件内容,二进制格式
"readBytes": func(fileName string) ([]byte, error) {
if f, err := openFileForRead(fileName); err == nil {
defer f.Close()
return f.ReadAllBytes()
}else{
return nil, err
}
},
// readFileLines 按行读取文件
// readFileLines return 文件内容,返回字符串数组
"readFileLines": func(fileName string) ([]string, error) {
if f, err := openFileForRead(fileName); err == nil {
defer f.Close()
return f.ReadLines()
}else{
return nil, err
}
},
// write 写入一个文件
// write content 文件内容,字符串格式
// write return 写入的字节数
"write": func(fileName, content string) (int, error) {
if f, err := openFileForWrite(fileName); err == nil {
defer f.Close()
return f.Write(content)
}else{
return 0, err
}
},
// writeBytes 写入一个二进制文件
// writeBytes content 文件内容,二进制格式
// writeBytes return 写入的字节数
"writeBytes": func(fileName string, content []byte) (int, error) {
if f, err := openFileForWrite(fileName); err == nil {
defer f.Close()
return f.WriteBytes(content)
}else{
return 0, err
}
},
"openForRead": openFileForRead,
"openForWrite": openFileForWrite,
"openForAppend": openFileForAppend,
"open": openFile,
// remove 删除文件
"remove": func(fileName string) error {
if !checkFileAllow(fileName) {
return errors.New(getNotAllowMessage())
}
return os.Remove(fileName)
},
// rename 修改文件名
// * fileOldName 旧文件
// * fileNewName 新文件
"rename": func(fileOldName, fileNewName string) error {
if !checkFileAllow(fileOldName) || !checkFileAllow(fileNewName) {
return errors.New(getNotAllowMessage())
}
return os.Rename(fileOldName, fileNewName)
},
// copy 复制文件
"copy": func(fileOldName, fileNewName string) error {
if !checkFileAllow(fileOldName) || !checkFileAllow(fileNewName) {
return errors.New(getNotAllowMessage())
}
if buf, err := u.ReadFileBytes(fileOldName); err == nil {
return u.WriteFileBytes(fileNewName, buf)
} else {
return err
}
},
// saveJson 将对象存储为JSON格式的文件
"saveJson": func(fileName string, content interface{}) error {
if !checkFileAllow(fileName) {
return errors.New(getNotAllowMessage())
}
return u.SaveJsonP(fileName, content)
},
// saveYaml 将对象存储为YAML格式的文件
"saveYaml": func(fileName string, content interface{}) error {
if !checkFileAllow(fileName) {
return errors.New(getNotAllowMessage())
}
return u.SaveYaml(fileName, content)
},
// loadJson 读取JSON格式的文件并转化为对象
// loadJson return 对象
"loadJson": func(fileName string) (interface{}, error) {
if !checkFileAllow(fileName) {
return nil, errors.New(getNotAllowMessage())
}
var data interface{}
err := u.LoadJson(fileName, &data)
return data, err
},
// loadYaml 读取YAML格式的文件并转化为对象
// loadYaml return 对象
"loadYaml": func(fileName string) (interface{}, error) {
if !checkFileAllow(fileName) {
return nil, errors.New(getNotAllowMessage())
}
var data interface{}
err := u.LoadYaml(fileName, &data)
return data, err
},
},
})
}
// openFileForRead 打开一个用于读取的文件,若不存在会抛出异常
// openFileForRead return 文件对象,请务必在使用完成后关闭文件
func openFileForRead(fileName string) (*File, error) {
return _openFile(fileName, os.O_RDONLY, 0400)
}
// openFileForWrite 打开一个用于写入的文件,若不存在会自动创建
// openFileForWrite return 文件对象,请务必在使用完成后关闭文件
func openFileForWrite(fileName string) (*File, error) {
return _openFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
}
// openFileForAppend 打开一个用于追加写入的文件,若不存在会自动创建
// openFileForAppend return 文件对象,请务必在使用完成后关闭文件
func openFileForAppend(fileName string) (*File, error) {
return _openFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
}
// openFile 打开一个用于追加写入的文件,若不存在会自动创建
// openFile return 文件对象,请务必在使用完成后关闭文件
func openFile(fileName string) (*File, error) {
return _openFile(fileName, os.O_CREATE|os.O_RDWR|os.O_SYNC, 0600)
}
func _openFile(fileName string, flag int, perm os.FileMode) (*File, error) {
if !checkFileAllow(fileName) {
return nil, errors.New(getNotAllowMessage())
}
u.CheckPath(fileName)
fd, err := os.OpenFile(fileName, flag, perm)
if err != nil {
return nil, err
}
lockFile(fd)
return &File{name: fileName, fd: fd}, nil
}
// Close 关闭文件
func (f *File) Close() error {
unlockFile(f.fd)
return f.fd.Close()
}
// Read 从文件中读取指定长度的内容
// * size 长度
// Read return 读取的内容,字符串格式
func (f *File) Read(size int) (string, error) {
str, err := f.ReadBytes(size)
return string(str), err
}
// ReadBytes 从二进制文件中读取指定长度的内容
// ReadBytes return 读取的内容,二进制格式
func (f *File) ReadBytes(size int) ([]byte, error) {
buf := make([]byte, size)
n, err := f.fd.Read(buf)
if err != nil {
return nil, err
}
return buf[0:n], nil
}
// ReadAll 从文件中读取全部内容
// ReadAll return 读取的内容,字符串格式
func (f *File) ReadAll() (string, error) {
str, err := f.ReadAllBytes()
return string(str), err
}
// ReadAllBytes 从二进制文件中读取全部内容
// ReadAllBytes return 读取的内容,二进制格式
func (f *File) ReadAllBytes() ([]byte, error) {
var maxLen int
if fi, _ := os.Stat(f.name); fi != nil {
maxLen = int(fi.Size())
} else {
maxLen = 1024000
}
return f.ReadBytes(maxLen)
}
// ReadLines 逐行文件中读取全部内容
// ReadLines return 读取的内容,字符串数组格式
func (f *File) ReadLines() ([]string, error) {
outs := make([]string, 0)
inputReader := bufio.NewReader(f.fd)
for {
line, err := inputReader.ReadString('\n')
line = strings.TrimRight(line, "\r\n")
outs = append(outs, line)
if err != nil {
break
}
}
return outs, nil
}
// Write 写入字符串
// Write return 写入的长度
func (f *File) Write(content string) (int, error) {
return f.WriteBytes([]byte(content))
}
// WriteBytes 写入二进制
// WriteBytes return 写入的长度
func (f *File) WriteBytes(content []byte) (int, error) {
return f.fd.Write(content)
}
// SeekStart 将文件指针移动到开头
func (f *File) SeekStart() error {
_, err := f.fd.Seek(0, 0)
return err
}
// SeekEnd 将文件指针移动到末尾
func (f *File) SeekEnd() error {
_, err := f.fd.Seek(0, 2)
return err
}
// Seek 将文件指针移动到指定位置(从文件开头计算)
func (f *File) Seek(offset int64) error {
_, err := f.fd.Seek(offset, 0)
return err
}
func getNotAllowMessage() string {
fileConfigLock.RLock()
defer fileConfigLock.RUnlock()
return notAllowMessage
}
func checkFileAllow(filename string) bool {
fileConfigLock.RLock()
defer fileConfigLock.RUnlock()
if len(allowPaths) > 0 {
ok := false
for _, allowPath := range allowPaths {
if strings.HasPrefix(filename, allowPath) {
ok = true
break
}
}
if !ok {
return false
}
}
if len(allowExtensions) > 0 {
ok := false
for _, allowExtension := range allowExtensions {
if strings.HasSuffix(filename, allowExtension) {
ok = true
break
}
}
if !ok {
return false
}
}
return true
}