Skip to content

Commit 83cd827

Browse files
authored
fix formUploader content-type bug (#131)
1 parent 697395b commit 83cd827

File tree

7 files changed

+44
-21
lines changed

7 files changed

+44
-21
lines changed

.github/workflows/ci-test.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ jobs:
6363
fail-fast: false
6464
max-parallel: 1
6565
matrix:
66-
go_version: ['1.11.x', '1.12.x', '1.13.x', '1.14.x', '1.15.x', '1.16.x', '1.17.x', '1.18.x', '1.19.x', '1.20.x', '1.21.x']
66+
go_version: ['1.11.x', '1.12.x', '1.13.x', '1.14.x', '1.15.x', '1.16.x', '1.17.x', '1.18.x', '1.19.x', '1.20.x', '1.21.x', '1.22.x']
6767
runs-on: ubuntu-latest
6868
permissions:
6969
actions: read
@@ -81,14 +81,14 @@ jobs:
8181
go-version: ${{ matrix.go_version }}
8282
- name: Format
8383
run: |
84-
if [ "${{ matrix.go_version }}" = "1.21.x" ]; then
84+
if [ "${{ matrix.go_version }}" = "1.22.x" ]; then
8585
if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then
8686
exit 1
8787
fi
8888
fi
8989
- name: Golint
9090
run: |
91-
if [ "${{ matrix.go_version }}" = "1.21.x" ]; then
91+
if [ "${{ matrix.go_version }}" = "1.22.x" ]; then
9292
set -e
9393
go install honnef.co/go/tools/cmd/staticcheck@latest
9494
make staticcheck

internal/clientv2/multipart.go

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package clientv2
22

33
import (
4+
"fmt"
45
"io"
56
"mime/multipart"
7+
"net/textproto"
8+
"strings"
69

710
"github.com/qiniu/go-sdk/v7/internal/context"
811
compatible_io "github.com/qiniu/go-sdk/v7/internal/io"
@@ -13,8 +16,8 @@ type (
1316
key, value string
1417
}
1518
keyFilePair struct {
16-
key, fileName string
17-
stream compatible_io.ReadSeekCloser
19+
key, fileName, contentType string
20+
stream compatible_io.ReadSeekCloser
1821
}
1922

2023
MultipartForm struct {
@@ -29,16 +32,17 @@ type (
2932
multipartWriter *multipart.Writer
3033
form *MultipartForm
3134
r *io.PipeReader
35+
closed bool
3236
}
3337
)
3438

3539
func (f *MultipartForm) SetValue(key, value string) *MultipartForm {
36-
f.values = append(f.values, keyValuePair{key: key, value: value})
40+
f.values = append(f.values, keyValuePair{key, value})
3741
return f
3842
}
3943

40-
func (f *MultipartForm) SetFile(key, fileName string, stream compatible_io.ReadSeekCloser) *MultipartForm {
41-
f.files = append(f.files, keyFilePair{key: key, fileName: fileName, stream: stream})
44+
func (f *MultipartForm) SetFile(key, fileName, contentType string, stream compatible_io.ReadSeekCloser) *MultipartForm {
45+
f.files = append(f.files, keyFilePair{key, fileName, contentType, stream})
4246
return f
4347
}
4448

@@ -67,7 +71,7 @@ func newMultipartFormReader(form *MultipartForm) *multipartFormReader {
6771
case <-ctx.Done():
6872
return
6973
default:
70-
if err := reader.createFormFile(pair.key, pair.fileName, pair.stream); err != nil {
74+
if err := reader.createFormFile(pair.key, pair.fileName, pair.contentType, pair.stream); err != nil {
7175
cancel(err)
7276
return
7377
}
@@ -88,6 +92,10 @@ func (r *multipartFormReader) Read(p []byte) (int, error) {
8892
}
8993

9094
func (r *multipartFormReader) Close() (err error) {
95+
if r.closed {
96+
return nil
97+
}
98+
r.closed = true
9199
r.form.cancel(io.ErrClosedPipe)
92100
err = r.r.Close()
93101
for _, pair := range r.form.files {
@@ -102,8 +110,11 @@ func (r *multipartFormReader) formDataContentType() string {
102110
return r.multipartWriter.FormDataContentType()
103111
}
104112

105-
func (r *multipartFormReader) createFormFile(fieldName, fileName string, stream compatible_io.ReadSeekCloser) error {
106-
if w, err := r.multipartWriter.CreateFormFile(fieldName, fileName); err != nil {
113+
func (r *multipartFormReader) createFormFile(fieldName, fileName, contentType string, stream compatible_io.ReadSeekCloser) error {
114+
h := make(textproto.MIMEHeader)
115+
h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"; filename="%s"`, escapeQuotes(fieldName), escapeQuotes(fileName)))
116+
h.Set("Content-Type", contentType)
117+
if w, err := r.multipartWriter.CreatePart(h); err != nil {
107118
return err
108119
} else if _, err := io.Copy(w, stream); err != nil {
109120
return err
@@ -133,3 +144,9 @@ func GetMultipartFormRequestBody(info *MultipartForm) GetRequestBody {
133144
return r, nil
134145
}
135146
}
147+
148+
var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
149+
150+
func escapeQuotes(s string) string {
151+
return quoteEscaper.Replace(s)
152+
}

internal/clientv2/multipart_test.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ func TestMultipart(t *testing.T) {
2626
form := new(MultipartForm).
2727
SetValue("test-1", "value-1").
2828
SetValue("test-2", "value-2").
29-
SetFile("test-file-1", "test-file-name-1", file1).
30-
SetFile("test-file-2", "test-file-name-2", file2)
29+
SetFile("test-file-1", "test-file-name-1", "application/json", file1).
30+
SetFile("test-file-2", "test-file-name-2", "application/x-www-form-urlencoded", file2)
3131
getRequestBody := GetMultipartFormRequestBody(form)
3232

3333
for i := 0; i < 5; i++ {
@@ -55,15 +55,15 @@ func TestMultipart(t *testing.T) {
5555
} else if f, err := os.Open(file1.Name()); err != nil {
5656
t.Fatal(err)
5757
} else {
58-
assertPartFile(t, part, "test-file-1", "test-file-name-1", f)
58+
assertPartFile(t, part, "test-file-1", "test-file-name-1", "application/json", f)
5959
f.Close()
6060
}
6161
if part, err := reader.NextPart(); err != nil {
6262
t.Fatal(err)
6363
} else if f, err := os.Open(file2.Name()); err != nil {
6464
t.Fatal(err)
6565
} else {
66-
assertPartFile(t, part, "test-file-2", "test-file-name-2", f)
66+
assertPartFile(t, part, "test-file-2", "test-file-name-2", "application/x-www-form-urlencoded", f)
6767
f.Close()
6868
}
6969
if _, err = reader.NextPart(); err != io.EOF {
@@ -109,13 +109,16 @@ func assertPartValue(t *testing.T, part *multipart.Part, key, value string) {
109109
}
110110
}
111111

112-
func assertPartFile(t *testing.T, part *multipart.Part, key, fileName string, value io.Reader) {
112+
func assertPartFile(t *testing.T, part *multipart.Part, key, fileName, contentType string, value io.Reader) {
113113
if part.FormName() != key {
114114
t.Fatalf("unexpected form name: %s != %s", part.FormName(), key)
115115
}
116116
if part.FileName() != fileName {
117117
t.Fatalf("unexpected file name: %s != %s", part.FileName(), fileName)
118118
}
119+
if acutalContentType := part.Header.Get("Content-Type"); acutalContentType != contentType {
120+
t.Fatalf("unexpected content type: %s != %s", acutalContentType, contentType)
121+
}
119122
md5Hasher := md5.New()
120123
if _, err := io.Copy(md5Hasher, part); err != nil {
121124
t.Fatal(err)

storage/form_upload.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,8 +207,9 @@ func (p *FormUploader) putSeekableData(ctx context.Context, ret interface{}, upT
207207
ObjectName: makeKeyForUploading(key, hasKey),
208208
UploadToken: uptoken.NewParser(upToken),
209209
File: http_client.MultipartFormBinaryData{
210-
Data: internal_io.MakeReadSeekCloserFromLimitedReader(fileReader, dataSize),
211-
Name: fileName,
210+
Data: internal_io.MakeReadSeekCloserFromLimitedReader(fileReader, dataSize),
211+
Name: fileName,
212+
ContentType: extra.MimeType,
212213
},
213214
CustomData: makeCustomData(extra.Params),
214215
ResponseBody: ret,

storagev2/apis/api_post_object.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func (form *innerPostObjectRequest) build(ctx context.Context) (*httpclient.Mult
4343
if form.File.Name == "" {
4444
return nil, errors.MissingRequiredFieldError{Name: "File.Name"}
4545
}
46-
multipartForm.SetFile("file", form.File.Name, form.File.Data)
46+
multipartForm.SetFile("file", form.File.Name, form.File.ContentType, form.File.Data)
4747
} else {
4848
return nil, errors.MissingRequiredFieldError{Name: "File"}
4949
}

storagev2/http_client/multipart.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package http_client
33
import compatible_io "github.com/qiniu/go-sdk/v7/internal/io"
44

55
type MultipartFormBinaryData struct {
6-
Data compatible_io.ReadSeekCloser
7-
Name string
6+
Data compatible_io.ReadSeekCloser
7+
Name string
8+
ContentType string
89
}

storagev2/internal/api-generator/multipart.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ func (mff *MultipartFormFields) addBuildFunc(group *jen.Group, structName string
195195
group.Add(jen.Id("multipartForm").Dot("SetFile").Call(
196196
jen.Lit(named.Key),
197197
jen.Id("form").Dot(fieldName).Dot("Name"),
198+
jen.Id("form").Dot(fieldName).Dot("ContentType"),
198199
jen.Id("form").Dot(fieldName).Dot("Data"),
199200
))
200201
}

0 commit comments

Comments
 (0)