Skip to content

Commit

Permalink
feat attachment filename support utf8 (gin-gonic#3071)
Browse files Browse the repository at this point in the history
  • Loading branch information
a-wing authored and daheige committed Apr 18, 2022
1 parent e836483 commit 7bd6e17
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 2 deletions.
7 changes: 5 additions & 2 deletions context.go
Expand Up @@ -6,7 +6,6 @@ package gin

import (
"errors"
"fmt"
"io"
"io/ioutil"
"log"
Expand Down Expand Up @@ -1015,7 +1014,11 @@ func (c *Context) FileFromFS(filepath string, fs http.FileSystem) {
// FileAttachment writes the specified file into the body stream in an efficient way
// On the client side, the file will typically be downloaded with the given filename
func (c *Context) FileAttachment(filepath, filename string) {
c.Writer.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
if isASCII(filename) {
c.Writer.Header().Set("Content-Disposition", `attachment; filename="`+filename+`"`)
} else {
c.Writer.Header().Set("Content-Disposition", `attachment; filename*=UTF-8''`+url.QueryEscape(filename))
}
http.ServeFile(c.Writer, c.Request, filepath)
}

Expand Down
14 changes: 14 additions & 0 deletions context_test.go
Expand Up @@ -15,6 +15,7 @@ import (
"net"
"net/http"
"net/http/httptest"
"net/url"
"os"
"reflect"
"strings"
Expand Down Expand Up @@ -1033,6 +1034,19 @@ func TestContextRenderAttachment(t *testing.T) {
assert.Equal(t, fmt.Sprintf("attachment; filename=\"%s\"", newFilename), w.Header().Get("Content-Disposition"))
}

func TestContextRenderUTF8Attachment(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
newFilename := "new🧡_filename.go"

c.Request, _ = http.NewRequest("GET", "/", nil)
c.FileAttachment("./gin.go", newFilename)

assert.Equal(t, 200, w.Code)
assert.Contains(t, w.Body.String(), "func New() *Engine {")
assert.Equal(t, `attachment; filename*=UTF-8''`+url.QueryEscape(newFilename), w.Header().Get("Content-Disposition"))
}

// TestContextRenderYAML tests that the response is serialized as YAML
// and Content-Type is set to application/x-yaml
func TestContextRenderYAML(t *testing.T) {
Expand Down
11 changes: 11 additions & 0 deletions utils.go
Expand Up @@ -12,6 +12,7 @@ import (
"reflect"
"runtime"
"strings"
"unicode"
)

// BindKey indicates a default bind key.
Expand Down Expand Up @@ -151,3 +152,13 @@ func resolveAddress(addr []string) string {
panic("too many parameters")
}
}

// https://stackoverflow.com/questions/53069040/checking-a-string-contains-only-ascii-characters
func isASCII(s string) bool {
for i := 0; i < len(s); i++ {
if s[i] > unicode.MaxASCII {
return false
}
}
return true
}
5 changes: 5 additions & 0 deletions utils_test.go
Expand Up @@ -143,3 +143,8 @@ func TestMarshalXMLforH(t *testing.T) {
e := h.MarshalXML(enc, x)
assert.Error(t, e)
}

func TestIsASCII(t *testing.T) {
assert.Equal(t, isASCII("test"), true)
assert.Equal(t, isASCII("🧡💛💚💙💜"), false)
}

0 comments on commit 7bd6e17

Please sign in to comment.