/
file_api_handler.go
158 lines (141 loc) · 4.39 KB
/
file_api_handler.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
package handler
import (
"io/fs"
"net/http"
"os"
"path/filepath"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/no-src/gofs/contract"
"github.com/no-src/gofs/logger"
"github.com/no-src/gofs/server"
"github.com/no-src/nsgo/fsutil"
"github.com/no-src/nsgo/hashutil"
)
type fileApiHandler struct {
logger *logger.Logger
root http.Dir
chunkSize int64
checkpointCount int
hash hashutil.Hash
}
// NewFileApiHandlerFunc returns a gin.HandlerFunc that queries the file info
func NewFileApiHandlerFunc(logger *logger.Logger, root http.Dir, chunkSize int64, checkpointCount int, hash hashutil.Hash) gin.HandlerFunc {
return (&fileApiHandler{
logger: logger,
root: root,
chunkSize: chunkSize,
checkpointCount: checkpointCount,
hash: hash,
}).Handle
}
func (h *fileApiHandler) Handle(c *gin.Context) {
defer func() {
e := recover()
if e != nil {
c.JSON(http.StatusOK, server.NewServerErrorResult())
}
}()
var fileList []contract.FileInfo
path := c.Query(contract.FsPath)
needHash := c.Query(contract.FsNeedHash) == contract.FsNeedHashValueTrue
needCheckpoint := c.Query(contract.FsNeedCheckpoint) == contract.ParamValueTrue
sourcePrefix := strings.Trim(server.SourceRoutePrefix, "/")
destPrefix := strings.Trim(server.DestRoutePrefix, "/")
if !strings.HasPrefix(strings.ToLower(path), sourcePrefix) && !strings.HasPrefix(strings.ToLower(path), destPrefix) {
c.JSON(http.StatusOK, server.NewErrorApiResult(-501, "must start with source or dest"))
return
}
path = filepath.Clean(path)
path = filepath.ToSlash(path)
if !strings.HasPrefix(strings.ToLower(path), sourcePrefix) && !strings.HasPrefix(strings.ToLower(path), destPrefix) {
c.JSON(http.StatusOK, server.NewErrorApiResult(-502, "invalid path"))
return
}
path = strings.TrimLeft(path, sourcePrefix)
f, err := h.root.Open(path)
if err != nil {
if os.IsNotExist(err) {
h.logger.Error(err, contract.NotFoundDesc)
c.JSON(http.StatusOK, server.NewErrorApiResult(contract.NotFound, contract.NotFoundDesc))
} else {
h.logger.Error(err, "file server open path error")
c.JSON(http.StatusOK, server.NewErrorApiResult(-503, "open path error"))
}
return
}
defer f.Close()
stat, err := f.Stat()
if err != nil {
h.logger.Error(err, "file server get file stat error")
c.JSON(http.StatusOK, server.NewErrorApiResult(-504, "get file stat error"))
return
}
if stat.IsDir() {
dirFileList, err := h.readDir(f, needHash, needCheckpoint, path)
if err != nil {
c.JSON(http.StatusOK, server.NewErrorApiResult(-505, "read dir error"))
return
}
fileList = append(fileList, dirFileList...)
}
c.JSON(http.StatusOK, server.NewApiResult(contract.Success, contract.SuccessDesc, fileList))
}
func (h *fileApiHandler) readDir(f http.File, needHash bool, needCheckpoint bool, path string) (fileList []contract.FileInfo, err error) {
files, err := f.Readdir(-1)
if err != nil {
h.logger.Error(err, "file server read dir error")
return fileList, err
}
for _, file := range files {
cTime, aTime, mTime, fsTimeErr := fsutil.GetFileTimeBySys(file.Sys())
if fsTimeErr != nil {
h.logger.Error(fsTimeErr, "get file times error => %s", file.Name())
cTime = time.Now()
aTime = cTime
mTime = cTime
}
hash := ""
var hvs hashutil.HashValues
if !file.IsDir() && (needHash || needCheckpoint) {
if cf, err := h.root.Open(filepath.ToSlash(filepath.Join(path, file.Name()))); err == nil {
if needCheckpoint {
hvs, _ = h.hash.CheckpointsHashFromFile(cf.(*os.File), h.chunkSize, h.checkpointCount)
}
if needHash {
if len(hvs) > 0 {
hash = hvs.Last().Hash
} else {
hash, _ = h.hash.HashFromFile(cf)
}
}
cf.Close()
}
}
fileList = append(fileList, contract.FileInfo{
Path: file.Name(),
IsDir: contract.ParseFsDirValue(file.IsDir()),
Size: file.Size(),
Hash: hash,
HashValues: hvs,
ATime: aTime.Unix(),
CTime: cTime.Unix(),
MTime: mTime.Unix(),
LinkTo: h.readlink(file),
})
}
return fileList, nil
}
func (h *fileApiHandler) readlink(file fs.FileInfo) string {
if fsutil.IsSymlinkMode(file.Mode()) {
path := filepath.Join(string(h.root), file.Name())
realPath, err := fsutil.Readlink(path)
if err == nil {
return realPath
}
h.logger.Error(err, "file api read link error => %s", path)
return file.Name()
}
return ""
}