Skip to content

File tree

4 files changed

+35
-2
lines changed

4 files changed

+35
-2
lines changed

context.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ package gin
66

77
import (
88
"errors"
9-
"fmt"
109
"io"
1110
"io/ioutil"
1211
"log"
@@ -1018,7 +1017,11 @@ func (c *Context) FileFromFS(filepath string, fs http.FileSystem) {
10181017
// FileAttachment writes the specified file into the body stream in an efficient way
10191018
// On the client side, the file will typically be downloaded with the given filename
10201019
func (c *Context) FileAttachment(filepath, filename string) {
1021-
c.Writer.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
1020+
if isASCII(filename) {
1021+
c.Writer.Header().Set("Content-Disposition", `attachment; filename="`+filename+`"`)
1022+
} else {
1023+
c.Writer.Header().Set("Content-Disposition", `attachment; filename*=UTF-8''`+url.QueryEscape(filename))
1024+
}
10221025
http.ServeFile(c.Writer, c.Request, filepath)
10231026
}
10241027

context_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"net"
1616
"net/http"
1717
"net/http/httptest"
18+
"net/url"
1819
"os"
1920
"reflect"
2021
"strings"
@@ -1033,6 +1034,19 @@ func TestContextRenderAttachment(t *testing.T) {
10331034
assert.Equal(t, fmt.Sprintf("attachment; filename=\"%s\"", newFilename), w.Header().Get("Content-Disposition"))
10341035
}
10351036

1037+
func TestContextRenderUTF8Attachment(t *testing.T) {
1038+
w := httptest.NewRecorder()
1039+
c, _ := CreateTestContext(w)
1040+
newFilename := "new🧡_filename.go"
1041+
1042+
c.Request, _ = http.NewRequest("GET", "/", nil)
1043+
c.FileAttachment("./gin.go", newFilename)
1044+
1045+
assert.Equal(t, 200, w.Code)
1046+
assert.Contains(t, w.Body.String(), "func New() *Engine {")
1047+
assert.Equal(t, `attachment; filename*=UTF-8''`+url.QueryEscape(newFilename), w.Header().Get("Content-Disposition"))
1048+
}
1049+
10361050
// TestContextRenderYAML tests that the response is serialized as YAML
10371051
// and Content-Type is set to application/x-yaml
10381052
func TestContextRenderYAML(t *testing.T) {

utils.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"reflect"
1313
"runtime"
1414
"strings"
15+
"unicode"
1516
)
1617

1718
// BindKey indicates a default bind key.
@@ -151,3 +152,13 @@ func resolveAddress(addr []string) string {
151152
panic("too many parameters")
152153
}
153154
}
155+
156+
// https://stackoverflow.com/questions/53069040/checking-a-string-contains-only-ascii-characters
157+
func isASCII(s string) bool {
158+
for i := 0; i < len(s); i++ {
159+
if s[i] > unicode.MaxASCII {
160+
return false
161+
}
162+
}
163+
return true
164+
}

utils_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,8 @@ func TestMarshalXMLforH(t *testing.T) {
143143
e := h.MarshalXML(enc, x)
144144
assert.Error(t, e)
145145
}
146+
147+
func TestIsASCII(t *testing.T) {
148+
assert.Equal(t, isASCII("test"), true)
149+
assert.Equal(t, isASCII("🧡💛💚💙💜"), false)
150+
}

0 commit comments

Comments
 (0)