Skip to content

Commit 4ac37b3

Browse files
committed
/api/raw reconstruction
1 parent 97a8e8f commit 4ac37b3

File tree

8 files changed

+252
-223
lines changed

8 files changed

+252
-223
lines changed

pkg/drives/base.go

+27
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ type ResourceService interface {
3131
PutHandler(w http.ResponseWriter, r *http.Request, d *common.Data) (int, error)
3232
PatchHandler(fileCache fileutils.FileCache) handleFunc
3333

34+
// raw handler
35+
RawHandler(w http.ResponseWriter, r *http.Request, d *common.Data) (int, error)
36+
3437
// paste funcs
3538
PasteSame(action, src, dst string, rename bool, fileCache fileutils.FileCache, w http.ResponseWriter, r *http.Request) error
3639
PasteDirFrom(fs afero.Fs, srcType, src, dstType, dst string, d *common.Data, fileMode os.FileMode, w http.ResponseWriter,
@@ -389,6 +392,30 @@ func (rs *BaseResourceService) PatchHandler(fileCache fileutils.FileCache) handl
389392
}
390393
}
391394

395+
func (rs *BaseResourceService) RawHandler(w http.ResponseWriter, r *http.Request, d *common.Data) (int, error) {
396+
file, err := files.NewFileInfo(files.FileOptions{
397+
Fs: files.DefaultFs,
398+
Path: r.URL.Path,
399+
Modify: true,
400+
Expand: false,
401+
ReadHeader: d.Server.TypeDetectionByHeader,
402+
})
403+
if err != nil {
404+
return common.ErrToStatus(err), err
405+
}
406+
407+
if files.IsNamedPipe(file.Mode) {
408+
SetContentDisposition(w, r, file)
409+
return 0, nil
410+
}
411+
412+
if !file.IsDir {
413+
return RawFileHandler(w, r, file)
414+
}
415+
416+
return RawDirHandler(w, r, d, file)
417+
}
418+
392419
func (rs *BaseResourceService) PasteSame(action, src, dst string, rename bool, fileCache fileutils.FileCache, w http.ResponseWriter, r *http.Request) error {
393420
return fmt.Errorf("Not Implemented")
394421
}

pkg/drives/cloud_drive.go

+14
Original file line numberDiff line numberDiff line change
@@ -1007,6 +1007,20 @@ func (rc *CloudDriveResourceService) PatchHandler(fileCache fileutils.FileCache)
10071007
}
10081008
}
10091009

1010+
func (rs *CloudDriveResourceService) RawHandler(w http.ResponseWriter, r *http.Request, d *common.Data) (int, error) {
1011+
bflName := r.Header.Get("X-Bfl-User")
1012+
src := r.URL.Path
1013+
metaData, err := GetCloudDriveMetadata(src, w, r)
1014+
if err != nil {
1015+
klog.Error(err)
1016+
return common.ErrToStatus(err), err
1017+
}
1018+
if metaData.IsDir {
1019+
return http.StatusNotImplemented, fmt.Errorf("doesn't support directory download for cloud drive now")
1020+
}
1021+
return RawFileHandlerCloudDrive(src, w, r, metaData, bflName)
1022+
}
1023+
10101024
func (rc *CloudDriveResourceService) PasteSame(action, src, dst string, rename bool, fileCache fileutils.FileCache, w http.ResponseWriter, r *http.Request) error {
10111025
switch action {
10121026
case "copy":

pkg/drives/google_drive.go

+14
Original file line numberDiff line numberDiff line change
@@ -1187,6 +1187,20 @@ func (rc *GoogleDriveResourceService) PatchHandler(fileCache fileutils.FileCache
11871187
}
11881188
}
11891189

1190+
func (rs *GoogleDriveResourceService) RawHandler(w http.ResponseWriter, r *http.Request, d *common.Data) (int, error) {
1191+
bflName := r.Header.Get("X-Bfl-User")
1192+
src := r.URL.Path
1193+
metaData, err := GetGoogleDriveMetadata(src, w, r)
1194+
if err != nil {
1195+
klog.Error(err)
1196+
return common.ErrToStatus(err), err
1197+
}
1198+
if metaData.IsDir {
1199+
return http.StatusNotImplemented, fmt.Errorf("doesn't support directory download for google drive now")
1200+
}
1201+
return RawFileHandlerGoogle(src, w, r, metaData, bflName)
1202+
}
1203+
11901204
func (rc *GoogleDriveResourceService) PasteSame(action, src, dst string, rename bool, fileCache fileutils.FileCache, w http.ResponseWriter, r *http.Request) error {
11911205
switch action {
11921206
case "copy":

pkg/drives/raw_base.go

+177
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
package drives
2+
3+
import (
4+
"errors"
5+
"files/pkg/common"
6+
"files/pkg/files"
7+
"files/pkg/fileutils"
8+
"github.com/mholt/archiver/v3"
9+
"k8s.io/klog/v2"
10+
"net/http"
11+
"net/url"
12+
gopath "path"
13+
"path/filepath"
14+
"strings"
15+
)
16+
17+
func slashClean(name string) string {
18+
if name == "" || name[0] != '/' {
19+
name = "/" + name
20+
}
21+
return gopath.Clean(name)
22+
}
23+
24+
func parseQueryFiles(r *http.Request, f *files.FileInfo) ([]string, error) {
25+
var fileSlice []string
26+
names := strings.Split(r.URL.Query().Get("files"), ",")
27+
28+
if len(names) == 0 {
29+
fileSlice = append(fileSlice, f.Path)
30+
} else {
31+
for _, name := range names {
32+
name, err := url.QueryUnescape(strings.Replace(name, "+", "%2B", -1))
33+
if err != nil {
34+
return nil, err
35+
}
36+
37+
name = slashClean(name)
38+
fileSlice = append(fileSlice, filepath.Join(f.Path, name))
39+
}
40+
}
41+
42+
return fileSlice, nil
43+
}
44+
45+
func parseQueryAlgorithm(r *http.Request) (string, archiver.Writer, error) {
46+
switch r.URL.Query().Get("algo") {
47+
case "zip", "true", "":
48+
return ".zip", archiver.NewZip(), nil
49+
case "tar":
50+
return ".tar", archiver.NewTar(), nil
51+
case "targz":
52+
return ".tar.gz", archiver.NewTarGz(), nil
53+
case "tarbz2":
54+
return ".tar.bz2", archiver.NewTarBz2(), nil
55+
case "tarxz":
56+
return ".tar.xz", archiver.NewTarXz(), nil
57+
case "tarlz4":
58+
return ".tar.lz4", archiver.NewTarLz4(), nil
59+
case "tarsz":
60+
return ".tar.sz", archiver.NewTarSz(), nil
61+
default:
62+
return "", nil, errors.New("format not implemented")
63+
}
64+
}
65+
66+
func SetContentDisposition(w http.ResponseWriter, r *http.Request, file *files.FileInfo) {
67+
if r.URL.Query().Get("inline") == "true" {
68+
w.Header().Set("Content-Disposition", "inline")
69+
} else {
70+
// As per RFC6266 section 4.3
71+
w.Header().Set("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(file.Name))
72+
}
73+
}
74+
75+
func AddFile(ar archiver.Writer, d *common.Data, path, commonPath string) error {
76+
info, err := files.DefaultFs.Stat(path)
77+
if err != nil {
78+
return err
79+
}
80+
81+
if !info.IsDir() && !info.Mode().IsRegular() {
82+
return nil
83+
}
84+
85+
file, err := files.DefaultFs.Open(path)
86+
if err != nil {
87+
return err
88+
}
89+
defer file.Close()
90+
91+
if path != commonPath {
92+
filename := strings.TrimPrefix(path, commonPath)
93+
filename = strings.TrimPrefix(filename, string(filepath.Separator))
94+
err = ar.Write(archiver.File{
95+
FileInfo: archiver.FileInfo{
96+
FileInfo: info,
97+
CustomName: filename,
98+
},
99+
ReadCloser: file,
100+
})
101+
if err != nil {
102+
return err
103+
}
104+
}
105+
106+
if info.IsDir() {
107+
names, err := file.Readdirnames(0)
108+
if err != nil {
109+
return err
110+
}
111+
112+
for _, name := range names {
113+
fPath := filepath.Join(path, name)
114+
err = AddFile(ar, d, fPath, commonPath)
115+
if err != nil {
116+
klog.Errorf("Failed to archive %s: %v", fPath, err)
117+
}
118+
}
119+
}
120+
121+
return nil
122+
}
123+
124+
func RawDirHandler(w http.ResponseWriter, r *http.Request, d *common.Data, file *files.FileInfo) (int, error) {
125+
filenames, err := parseQueryFiles(r, file)
126+
if err != nil {
127+
return http.StatusInternalServerError, err
128+
}
129+
130+
extension, ar, err := parseQueryAlgorithm(r)
131+
if err != nil {
132+
return http.StatusInternalServerError, err
133+
}
134+
135+
err = ar.Create(w)
136+
if err != nil {
137+
return http.StatusInternalServerError, err
138+
}
139+
defer ar.Close()
140+
141+
commonDir := fileutils.CommonPrefix(filepath.Separator, filenames...)
142+
143+
name := filepath.Base(commonDir)
144+
if name == "." || name == "" || name == string(filepath.Separator) {
145+
name = file.Name
146+
}
147+
// Prefix used to distinguish a filelist generated
148+
// archive from the full directory archive
149+
if len(filenames) > 1 {
150+
name = "_" + name
151+
}
152+
name += extension
153+
w.Header().Set("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(name))
154+
155+
for _, fname := range filenames {
156+
err = AddFile(ar, d, fname, commonDir)
157+
if err != nil {
158+
klog.Errorf("Failed to archive %s: %v", fname, err)
159+
}
160+
}
161+
162+
return 0, nil
163+
}
164+
165+
func RawFileHandler(w http.ResponseWriter, r *http.Request, file *files.FileInfo) (int, error) {
166+
fd, err := file.Fs.Open(file.Path)
167+
if err != nil {
168+
return http.StatusInternalServerError, err
169+
}
170+
defer fd.Close()
171+
172+
SetContentDisposition(w, r, file)
173+
174+
w.Header().Set("Cache-Control", "private")
175+
http.ServeContent(w, r, file.Name, file.ModTime, fd)
176+
return 0, nil
177+
}

pkg/drives/sync.go

+4
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,10 @@ func (rc *SyncResourceService) PatchHandler(fileCache fileutils.FileCache) handl
181181
}
182182
}
183183

184+
func (rs *SyncResourceService) RawHandler(w http.ResponseWriter, r *http.Request, d *common.Data) (int, error) {
185+
return http.StatusNotImplemented, nil
186+
}
187+
184188
func (rc *SyncResourceService) PasteSame(action, src, dst string, rename bool, fileCache fileutils.FileCache, w http.ResponseWriter, r *http.Request) error {
185189
return ResourceSyncPatch(action, src, dst, r)
186190
}

pkg/http/http.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func NewHandler(
5858
api.PathPrefix("/share/share_link").Handler(monkey(shareLinkPostHandler, "/api/share/share_link")).Methods("POST")
5959
api.PathPrefix("/share/share_link").Handler(monkey(shareLinkDeleteHandler, "/api/share/share_link")).Methods("DELETE")
6060

61-
api.PathPrefix("/raw").Handler(monkey(rawHandler, "/api/raw")).Methods("GET")
61+
api.PathPrefix("/raw").Handler(monkey(RawHandler, "/api/raw")).Methods("GET")
6262
api.PathPrefix("/md5").Handler(monkey(md5Handler, "/api/md5")).Methods("GET")
6363
api.PathPrefix("/permission").Handler(monkey(permissionGetHandler, "/api/permission")).Methods("GET")
6464
api.PathPrefix("/permission").Handler(monkey(permissionPutHandler, "/api/permission")).Methods("PUT")

pkg/http/preview.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func previewHandler(imgSvc preview.ImgService, fileCache fileutils.FileCache, en
5252
return common.ErrToStatus(err), err
5353
}
5454

55-
setContentDisposition(w, r, file)
55+
drives.SetContentDisposition(w, r, file)
5656

5757
switch file.Type {
5858
case "image":
@@ -74,13 +74,13 @@ func HandleImagePreview(
7474
) (int, error) {
7575
if (previewSize == preview.PreviewSizeBig && !resizePreview) ||
7676
(previewSize == preview.PreviewSizeThumb && !enableThumbnails) {
77-
return rawFileHandler(w, r, file)
77+
return drives.RawFileHandler(w, r, file)
7878
}
7979

8080
format, err := imgSvc.FormatFromExtension(file.Extension)
8181
// Unsupported extensions directly return the raw data
8282
if err == img.ErrUnsupportedFormat || format == img.FormatGif {
83-
return rawFileHandler(w, r, file)
83+
return drives.RawFileHandler(w, r, file)
8484
}
8585
if err != nil {
8686
return common.ErrToStatus(err), err
@@ -100,7 +100,7 @@ func HandleImagePreview(
100100
resizedImage, err = preview.CreatePreview(imgSvc, fileCache, file, previewSize, 2)
101101
if err != nil {
102102
klog.Infoln("second method failed!")
103-
return rawFileHandler(w, r, file)
103+
return drives.RawFileHandler(w, r, file)
104104
}
105105
}
106106
}

0 commit comments

Comments
 (0)