added ?download file serve query param support to force file download
This commit is contained in:
parent
7e0a4e61b4
commit
50d7df45eb
|
@ -87,6 +87,8 @@
|
||||||
- **!** renamed `models.RequestData` to `models.RequestInfo` and soft-deprecated `apis.RequestData(c)` to `apis.RequestInfo(c)` to avoid the stuttering with the `Data` field.
|
- **!** renamed `models.RequestData` to `models.RequestInfo` and soft-deprecated `apis.RequestData(c)` to `apis.RequestInfo(c)` to avoid the stuttering with the `Data` field.
|
||||||
_The old `apis.RequestData()` method still works to minimize the breaking changes but it is recommended to replace it with `apis.RequestInfo(c)`._
|
_The old `apis.RequestData()` method still works to minimize the breaking changes but it is recommended to replace it with `apis.RequestInfo(c)`._
|
||||||
|
|
||||||
|
- Added `?download` file query parameter option to instruct the browser to always download a file and not show a preview.
|
||||||
|
|
||||||
|
|
||||||
## v0.16.10
|
## v0.16.10
|
||||||
|
|
||||||
|
|
|
@ -321,7 +321,14 @@ var manualExtensionContentTypes = map[string]string{
|
||||||
".css": "text/css", // (see https://github.com/gabriel-vasile/mimetype/pull/113)
|
".css": "text/css", // (see https://github.com/gabriel-vasile/mimetype/pull/113)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// forceAttachmentParam is the name of the request query parameter to
|
||||||
|
// force "Content-Disposition: attachment" header.
|
||||||
|
const forceAttachmentParam = "download"
|
||||||
|
|
||||||
// Serve serves the file at fileKey location to an HTTP response.
|
// Serve serves the file at fileKey location to an HTTP response.
|
||||||
|
//
|
||||||
|
// If the `download` query parameter is used the file will be always served for
|
||||||
|
// download no matter of its type (aka. with "Content-Disposition: attachment").
|
||||||
func (s *System) Serve(res http.ResponseWriter, req *http.Request, fileKey string, name string) error {
|
func (s *System) Serve(res http.ResponseWriter, req *http.Request, fileKey string, name string) error {
|
||||||
br, readErr := s.bucket.NewReader(s.ctx, fileKey, nil)
|
br, readErr := s.bucket.NewReader(s.ctx, fileKey, nil)
|
||||||
if readErr != nil {
|
if readErr != nil {
|
||||||
|
@ -329,9 +336,11 @@ func (s *System) Serve(res http.ResponseWriter, req *http.Request, fileKey strin
|
||||||
}
|
}
|
||||||
defer br.Close()
|
defer br.Close()
|
||||||
|
|
||||||
|
forceAttachment := req.URL.Query().Has(forceAttachmentParam)
|
||||||
|
|
||||||
disposition := "attachment"
|
disposition := "attachment"
|
||||||
realContentType := br.ContentType()
|
realContentType := br.ContentType()
|
||||||
if list.ExistInSlice(realContentType, inlineServeContentTypes) {
|
if !forceAttachment && list.ExistInSlice(realContentType, inlineServeContentTypes) {
|
||||||
disposition = "inline"
|
disposition = "inline"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -257,7 +257,8 @@ func TestFileSystemServe(t *testing.T) {
|
||||||
scenarios := []struct {
|
scenarios := []struct {
|
||||||
path string
|
path string
|
||||||
name string
|
name string
|
||||||
customHeaders map[string]string
|
query map[string]string
|
||||||
|
headers map[string]string
|
||||||
expectError bool
|
expectError bool
|
||||||
expectHeaders map[string]string
|
expectHeaders map[string]string
|
||||||
}{
|
}{
|
||||||
|
@ -266,6 +267,7 @@ func TestFileSystemServe(t *testing.T) {
|
||||||
"missing.txt",
|
"missing.txt",
|
||||||
"test_name.txt",
|
"test_name.txt",
|
||||||
nil,
|
nil,
|
||||||
|
nil,
|
||||||
true,
|
true,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
@ -274,6 +276,7 @@ func TestFileSystemServe(t *testing.T) {
|
||||||
"test/sub1.txt",
|
"test/sub1.txt",
|
||||||
"test_name.txt",
|
"test_name.txt",
|
||||||
nil,
|
nil,
|
||||||
|
nil,
|
||||||
false,
|
false,
|
||||||
map[string]string{
|
map[string]string{
|
||||||
"Content-Disposition": "attachment; filename=test_name.txt",
|
"Content-Disposition": "attachment; filename=test_name.txt",
|
||||||
|
@ -288,6 +291,7 @@ func TestFileSystemServe(t *testing.T) {
|
||||||
"image.png",
|
"image.png",
|
||||||
"test_name.png",
|
"test_name.png",
|
||||||
nil,
|
nil,
|
||||||
|
nil,
|
||||||
false,
|
false,
|
||||||
map[string]string{
|
map[string]string{
|
||||||
"Content-Disposition": "inline; filename=test_name.png",
|
"Content-Disposition": "inline; filename=test_name.png",
|
||||||
|
@ -297,11 +301,27 @@ func TestFileSystemServe(t *testing.T) {
|
||||||
"Cache-Control": cacheControl,
|
"Cache-Control": cacheControl,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// png with forced attachment
|
||||||
|
"image.png",
|
||||||
|
"test_name_download.png",
|
||||||
|
map[string]string{"download": "12"},
|
||||||
|
nil,
|
||||||
|
false,
|
||||||
|
map[string]string{
|
||||||
|
"Content-Disposition": "attachment; filename=test_name_download.png",
|
||||||
|
"Content-Type": "image/png",
|
||||||
|
"Content-Length": "73",
|
||||||
|
"Content-Security-Policy": csp,
|
||||||
|
"Cache-Control": cacheControl,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
// svg exception
|
// svg exception
|
||||||
"image.svg",
|
"image.svg",
|
||||||
"test_name.svg",
|
"test_name.svg",
|
||||||
nil,
|
nil,
|
||||||
|
nil,
|
||||||
false,
|
false,
|
||||||
map[string]string{
|
map[string]string{
|
||||||
"Content-Disposition": "attachment; filename=test_name.svg",
|
"Content-Disposition": "attachment; filename=test_name.svg",
|
||||||
|
@ -316,6 +336,7 @@ func TestFileSystemServe(t *testing.T) {
|
||||||
"style.css",
|
"style.css",
|
||||||
"test_name.css",
|
"test_name.css",
|
||||||
nil,
|
nil,
|
||||||
|
nil,
|
||||||
false,
|
false,
|
||||||
map[string]string{
|
map[string]string{
|
||||||
"Content-Disposition": "attachment; filename=test_name.css",
|
"Content-Disposition": "attachment; filename=test_name.css",
|
||||||
|
@ -329,6 +350,7 @@ func TestFileSystemServe(t *testing.T) {
|
||||||
// custom header
|
// custom header
|
||||||
"test/sub2.txt",
|
"test/sub2.txt",
|
||||||
"test_name.txt",
|
"test_name.txt",
|
||||||
|
nil,
|
||||||
map[string]string{
|
map[string]string{
|
||||||
"Content-Disposition": "1",
|
"Content-Disposition": "1",
|
||||||
"Content-Type": "2",
|
"Content-Type": "2",
|
||||||
|
@ -353,7 +375,13 @@ func TestFileSystemServe(t *testing.T) {
|
||||||
res := httptest.NewRecorder()
|
res := httptest.NewRecorder()
|
||||||
req := httptest.NewRequest("GET", "/", nil)
|
req := httptest.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
for k, v := range s.customHeaders {
|
query := req.URL.Query()
|
||||||
|
for k, v := range s.query {
|
||||||
|
query.Set(k, v)
|
||||||
|
}
|
||||||
|
req.URL.RawQuery = query.Encode()
|
||||||
|
|
||||||
|
for k, v := range s.headers {
|
||||||
res.Header().Set(k, v)
|
res.Header().Set(k, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue