Skip to content

Commit 5327f14

Browse files
committed
feat(serverHandler): allow specify filename for download or archive
1 parent aeb5224 commit 5327f14

File tree

12 files changed

+112
-93
lines changed

12 files changed

+112
-93
lines changed

doc/en-US/api.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,18 @@ Example:
5050
wget --recursive -nc -nH -np 'http://localhost/dir/?download'
5151
```
5252

53-
# Add download param to file
53+
# Add download param to files
5454
```
5555
GET <path>?download[&sort=key]
5656
```
57-
Add download parameter to file links in the page, which
58-
make them downloadable instead of displaying content.
57+
Add download parameter to file links in directory list page, which
58+
makes them downloadable instead of displaying content.
5959

6060
# Download a file
6161
Notify user agent download a file rather than displaying its content,
6262
by outputting `Content-Disposition` header.
6363
```
64-
GET <path/to/file>?download
64+
GET <path/to/file>?download[=filename]
6565
```
6666

6767
Example:
@@ -72,12 +72,12 @@ curl 'http://localhost/ghfs/file?download'
7272
# Get contents of specified path as archive file
7373
Only work when "archive" is enabled.
7474
```
75-
GET <path>?tar
76-
GET <path>?tgz
77-
GET <path>?zip
78-
POST <path>?tar
79-
POST <path>?tgz
80-
POST <path>?zip
75+
GET <path>?tar[=filename]
76+
GET <path>?tgz[=filename]
77+
GET <path>?zip[=filename]
78+
POST <path>?tar[=filename]
79+
POST <path>?tgz[=filename]
80+
POST <path>?zip[=filename]
8181
```
8282

8383
Example:

doc/zh-CN/api.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,10 @@ GET <path>?download[&sort=key]
5555
```
5656
为页面中的文件链接添加下载参数,使其可被下载,而不是显示其内容。
5757

58-
# 下载文件
58+
# 下载单个文件
5959
通过输出`Content-Disposition`头,通知用户代理下载文件而不是显示其内容。
6060
```
61-
GET <path/to/file>?download
61+
GET <path/to/file>?download[=filename]
6262
```
6363

6464
举例:
@@ -69,12 +69,12 @@ curl 'http://localhost/ghfs/file?download'
6969
# 以打包文件形式获取指定路径下的内容
7070
仅在“archive”选项启用时有效。
7171
```
72-
GET <path>?tar
73-
GET <path>?tgz
74-
GET <path>?zip
75-
POST <path>?tar
76-
POST <path>?tgz
77-
POST <path>?zip
72+
GET <path>?tar[=filename]
73+
GET <path>?tgz[=filename]
74+
GET <path>?zip[=filename]
75+
POST <path>?tar[=filename]
76+
POST <path>?tgz[=filename]
77+
POST <path>?zip[=filename]
7878
```
7979

8080
举例:

src/serverHandler/archive.go

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -142,21 +142,10 @@ func (h *aliasHandler) archiveFiles(
142142
session *sessionContext,
143143
data *responseData,
144144
selections []string,
145-
fileSuffix string,
146145
contentType string,
147146
cbWriteFile archiveCallback,
148147
) {
149-
var itemName string
150-
_, hasAlias := h.aliases.byUrlPath(session.vhostReqPath)
151-
if hasAlias {
152-
itemName = path.Base(session.vhostReqPath)
153-
}
154-
if len(itemName) == 0 || itemName == "/" {
155-
itemName = data.ItemName
156-
}
157-
158-
targetFilename := itemName + fileSuffix
159-
writeArchiveHeader(w, contentType, targetFilename)
148+
writeArchiveHeader(w, contentType, session.outFileName)
160149

161150
if !NeedResponseBody(r.Method) {
162151
return
@@ -170,7 +159,7 @@ func (h *aliasHandler) archiveFiles(
170159
data.Item != nil, // not empty root
171160
selections,
172161
func(f *os.File, fInfo os.FileInfo, relPath string) error {
173-
h.logArchive(targetFilename, relPath, r)
162+
h.logArchive(session.outFileName, relPath, r)
174163
err := cbWriteFile(f, fInfo, relPath)
175164
h.logError(err)
176165
return err

src/serverHandler/archiveTar.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,7 @@ func (h *aliasHandler) tar(w http.ResponseWriter, r *http.Request, session *sess
7777
session,
7878
data,
7979
selections,
80-
".tar",
81-
"application/octet-stream",
80+
"application/x-tar",
8281
func(f *os.File, fInfo os.FileInfo, relPath string) error {
8382
return writeTar(tw, f, fInfo, relPath)
8483
},
@@ -120,8 +119,7 @@ func (h *aliasHandler) tgz(w http.ResponseWriter, r *http.Request, session *sess
120119
session,
121120
data,
122121
selections,
123-
".tar.gz",
124-
"application/octet-stream",
122+
"application/gzip",
125123
func(f *os.File, fInfo os.FileInfo, relPath string) error {
126124
return writeTar(tw, f, fInfo, relPath)
127125
},

src/serverHandler/archiveZip.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ func (h *aliasHandler) zip(w http.ResponseWriter, r *http.Request, session *sess
6161
session,
6262
data,
6363
selections,
64-
".zip",
6564
"application/zip",
6665
func(f *os.File, fInfo os.FileInfo, relPath string) error {
6766
return writeZip(zipWriter, f, fInfo, relPath)

src/serverHandler/auth.go

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -55,24 +55,18 @@ func (h *aliasHandler) verifyAuth(r *http.Request, vhostReqPath, reqFsPath strin
5555
}
5656

5757
func (h *aliasHandler) extractNoAuthUrl(r *http.Request, session *sessionContext, data *responseData) string {
58-
if session.query.Has(authQueryParam) {
59-
returnUrl := session.query.Get(authQueryParam)
60-
61-
if len(returnUrl) > 0 {
62-
url, err := url.QueryUnescape(returnUrl)
63-
if err == nil {
64-
returnUrl = url
65-
}
66-
}
67-
68-
if len(returnUrl) > 0 {
58+
returnUrl, hasReturnUrl := getUrlValue(session.query, authQueryParam)
59+
if hasReturnUrl && len(returnUrl) > 0 {
60+
var err error
61+
returnUrl, err = url.QueryUnescape(returnUrl)
62+
if err == nil {
6963
return returnUrl
7064
}
7165
}
7266

73-
referrer := r.Header.Get("Referer")
74-
if len(referrer) > 0 {
75-
return referrer
67+
returnUrl = r.Header.Get("Referer")
68+
if len(returnUrl) > 0 {
69+
return returnUrl
7670
}
7771

7872
return session.prefixReqPath + data.Context.QueryString()

src/serverHandler/content.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ func (h *aliasHandler) content(w http.ResponseWriter, r *http.Request, session *
1010
header.Set("Vary", session.vary)
1111
header.Set("X-Content-Type-Options", "nosniff")
1212
if data.IsDownload {
13-
filename := url.PathEscape(data.ItemName)
13+
filename := url.PathEscape(session.outFileName)
1414
header.Set("Content-Disposition", "attachment; filename="+filename+"; filename*=UTF-8''"+filename)
1515
}
1616

src/serverHandler/sessionData.go

Lines changed: 60 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ type sessionContext struct {
8484

8585
query url.Values
8686

87+
outFileName string
88+
8789
errors []error
8890
}
8991

@@ -110,7 +112,6 @@ type responseData struct {
110112
RootRelPath string
111113

112114
Item os.FileInfo
113-
ItemName string
114115
SubItems []os.FileInfo
115116
AliasSubItems []os.FileInfo
116117
SubItemsHtml []itemHtml
@@ -267,7 +268,12 @@ func getSubItemPrefix(currDirRelPath, rawRequestPath string, tailSlash bool) str
267268
}
268269
}
269270

270-
func getItemName(info os.FileInfo, r *http.Request) (itemName string) {
271+
func getItemName(info os.FileInfo, r *http.Request, prefixReqPath string) (itemName string) {
272+
itemName = path.Base(prefixReqPath)
273+
if len(itemName) > 0 && itemName != "/" {
274+
return
275+
}
276+
271277
if info != nil {
272278
itemName = info.Name()
273279
}
@@ -372,39 +378,6 @@ func (h *aliasHandler) getSessionData(r *http.Request) (session *sessionContext,
372378

373379
headers := h.getHeaders(vhostReqPath, fsPath, authSuccess)
374380

375-
isMutate := false
376-
isUpload := false
377-
isMkdir := false
378-
isDelete := false
379-
switch {
380-
case query.Has("upload"):
381-
isMutate = true
382-
isUpload = true
383-
case query.Has("mkdir"):
384-
isMutate = true
385-
isMkdir = true
386-
case query.Has("delete"):
387-
isMutate = true
388-
isDelete = true
389-
}
390-
391-
isArchive := false
392-
var arFmt archiveFormat
393-
switch {
394-
case query.Has("tar"):
395-
isArchive = true
396-
arFmt = tarFmt
397-
case query.Has("tgz"):
398-
isArchive = true
399-
arFmt = tgzFmt
400-
case query.Has("zip"):
401-
isArchive = true
402-
arFmt = zipFmt
403-
}
404-
405-
isSimple := query.Has("simple")
406-
isDownload := query.Has("download")
407-
408381
accepts := acceptHeaders.ParseAccepts(r.Header.Get("Accept"))
409382
acceptIndex, _, _ := accepts.GetPreferredValue(acceptContentTypes)
410383
wantJson := acceptIndex == acceptJsonIndex
@@ -459,7 +432,56 @@ func (h *aliasHandler) getSessionData(r *http.Request) (session *sessionContext,
459432

460433
canIndex = canIndex && allowAccess
461434

462-
itemName := getItemName(item, r)
435+
itemName := getItemName(item, r, prefixReqPath)
436+
437+
isMutate := false
438+
isUpload := false
439+
isMkdir := false
440+
isDelete := false
441+
switch {
442+
case query.Has("upload"):
443+
isMutate = true
444+
isUpload = true
445+
case query.Has("mkdir"):
446+
isMutate = true
447+
isMkdir = true
448+
case query.Has("delete"):
449+
isMutate = true
450+
isDelete = true
451+
}
452+
453+
var outFileName string
454+
455+
isSimple := query.Has("simple")
456+
dlName, isDownload := getUrlValue(query, "download")
457+
if isDownload {
458+
if len(dlName) > 0 {
459+
outFileName = dlName
460+
} else {
461+
outFileName = itemName
462+
}
463+
}
464+
465+
isArchive := false
466+
var arName string
467+
var arFmt archiveFormat
468+
if arName, isArchive = getUrlValue(query, "tar"); isArchive {
469+
outFileName = ".tar"
470+
arFmt = tarFmt
471+
} else if arName, isArchive = getUrlValue(query, "tgz"); isArchive {
472+
outFileName = ".tar.tz"
473+
arFmt = tgzFmt
474+
} else if arName, isArchive = getUrlValue(query, "zip"); isArchive {
475+
outFileName = ".zip"
476+
arFmt = zipFmt
477+
}
478+
if isArchive {
479+
if len(arName) > 0 {
480+
outFileName = arName
481+
} else {
482+
outFileName = itemName + outFileName
483+
}
484+
}
463485

464486
subItems, _readdirErr := readdir(file, item, canIndex && !isMutate && !isArchive && NeedResponseBody(r.Method))
465487
if _readdirErr != nil {
@@ -532,6 +554,8 @@ func (h *aliasHandler) getSessionData(r *http.Request) (session *sessionContext,
532554
isArchive: isArchive,
533555
archiveFormat: arFmt,
534556

557+
outFileName: outFileName,
558+
535559
file: file,
536560

537561
query: query,
@@ -561,7 +585,6 @@ func (h *aliasHandler) getSessionData(r *http.Request) (session *sessionContext,
561585
RootRelPath: rootRelPath,
562586

563587
Item: item,
564-
ItemName: itemName,
565588
SubItems: subItems,
566589
AliasSubItems: aliasSubItems,
567590
SubItemsHtml: nil,

src/serverHandler/util.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,22 @@ import (
44
"mjpclab.dev/ghfs/src/user"
55
"mjpclab.dev/ghfs/src/util"
66
"net/http"
7+
"net/url"
78
"os"
89
"path"
910
"regexp"
1011
"strings"
1112
)
1213

14+
func getUrlValue(urlValues url.Values, key string) (value string, ok bool) {
15+
var values []string
16+
values, ok = urlValues[key]
17+
if len(values) > 0 {
18+
value = values[len(values)-1]
19+
}
20+
return
21+
}
22+
1323
func pathUsernamesToPathUids(users *user.List, pathsUsernames [][]string) pathIntsList {
1424
list := make(pathIntsList, 0, len(pathsUsernames))
1525
for _, pathUsernames := range pathsUsernames {

src/tpl/defaultTheme/frontend/index.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@
6969

7070
{{if .CanArchive}}
7171
<div class="archive">
72-
<a href="{{.SubItemPrefix}}?tar" download="{{.ItemName}}.tar">.tar</a>
73-
<a href="{{.SubItemPrefix}}?tgz" download="{{.ItemName}}.tar.gz">.tar.gz</a>
74-
<a href="{{.SubItemPrefix}}?zip" download="{{.ItemName}}.zip">.zip</a>
72+
<a href="{{.SubItemPrefix}}?tar">.tar</a>
73+
<a href="{{.SubItemPrefix}}?tgz">.tar.gz</a>
74+
<a href="{{.SubItemPrefix}}?zip">.zip</a>
7575
</div>
7676
{{end}}
7777

0 commit comments

Comments
 (0)