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.
 | 
			
		||||
  _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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -321,7 +321,14 @@ var manualExtensionContentTypes = map[string]string{
 | 
			
		|||
	".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.
 | 
			
		||||
//
 | 
			
		||||
// 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 {
 | 
			
		||||
	br, readErr := s.bucket.NewReader(s.ctx, fileKey, nil)
 | 
			
		||||
	if readErr != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -329,9 +336,11 @@ func (s *System) Serve(res http.ResponseWriter, req *http.Request, fileKey strin
 | 
			
		|||
	}
 | 
			
		||||
	defer br.Close()
 | 
			
		||||
 | 
			
		||||
	forceAttachment := req.URL.Query().Has(forceAttachmentParam)
 | 
			
		||||
 | 
			
		||||
	disposition := "attachment"
 | 
			
		||||
	realContentType := br.ContentType()
 | 
			
		||||
	if list.ExistInSlice(realContentType, inlineServeContentTypes) {
 | 
			
		||||
	if !forceAttachment && list.ExistInSlice(realContentType, inlineServeContentTypes) {
 | 
			
		||||
		disposition = "inline"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -257,7 +257,8 @@ func TestFileSystemServe(t *testing.T) {
 | 
			
		|||
	scenarios := []struct {
 | 
			
		||||
		path          string
 | 
			
		||||
		name          string
 | 
			
		||||
		customHeaders map[string]string
 | 
			
		||||
		query         map[string]string
 | 
			
		||||
		headers       map[string]string
 | 
			
		||||
		expectError   bool
 | 
			
		||||
		expectHeaders map[string]string
 | 
			
		||||
	}{
 | 
			
		||||
| 
						 | 
				
			
			@ -266,6 +267,7 @@ func TestFileSystemServe(t *testing.T) {
 | 
			
		|||
			"missing.txt",
 | 
			
		||||
			"test_name.txt",
 | 
			
		||||
			nil,
 | 
			
		||||
			nil,
 | 
			
		||||
			true,
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
| 
						 | 
				
			
			@ -274,6 +276,7 @@ func TestFileSystemServe(t *testing.T) {
 | 
			
		|||
			"test/sub1.txt",
 | 
			
		||||
			"test_name.txt",
 | 
			
		||||
			nil,
 | 
			
		||||
			nil,
 | 
			
		||||
			false,
 | 
			
		||||
			map[string]string{
 | 
			
		||||
				"Content-Disposition":     "attachment; filename=test_name.txt",
 | 
			
		||||
| 
						 | 
				
			
			@ -288,6 +291,7 @@ func TestFileSystemServe(t *testing.T) {
 | 
			
		|||
			"image.png",
 | 
			
		||||
			"test_name.png",
 | 
			
		||||
			nil,
 | 
			
		||||
			nil,
 | 
			
		||||
			false,
 | 
			
		||||
			map[string]string{
 | 
			
		||||
				"Content-Disposition":     "inline; filename=test_name.png",
 | 
			
		||||
| 
						 | 
				
			
			@ -297,11 +301,27 @@ func TestFileSystemServe(t *testing.T) {
 | 
			
		|||
				"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
 | 
			
		||||
			"image.svg",
 | 
			
		||||
			"test_name.svg",
 | 
			
		||||
			nil,
 | 
			
		||||
			nil,
 | 
			
		||||
			false,
 | 
			
		||||
			map[string]string{
 | 
			
		||||
				"Content-Disposition":     "attachment; filename=test_name.svg",
 | 
			
		||||
| 
						 | 
				
			
			@ -316,6 +336,7 @@ func TestFileSystemServe(t *testing.T) {
 | 
			
		|||
			"style.css",
 | 
			
		||||
			"test_name.css",
 | 
			
		||||
			nil,
 | 
			
		||||
			nil,
 | 
			
		||||
			false,
 | 
			
		||||
			map[string]string{
 | 
			
		||||
				"Content-Disposition":     "attachment; filename=test_name.css",
 | 
			
		||||
| 
						 | 
				
			
			@ -329,6 +350,7 @@ func TestFileSystemServe(t *testing.T) {
 | 
			
		|||
			// custom header
 | 
			
		||||
			"test/sub2.txt",
 | 
			
		||||
			"test_name.txt",
 | 
			
		||||
			nil,
 | 
			
		||||
			map[string]string{
 | 
			
		||||
				"Content-Disposition":     "1",
 | 
			
		||||
				"Content-Type":            "2",
 | 
			
		||||
| 
						 | 
				
			
			@ -353,7 +375,13 @@ func TestFileSystemServe(t *testing.T) {
 | 
			
		|||
		res := httptest.NewRecorder()
 | 
			
		||||
		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)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue