github.com/qiniu/go-sdk/v7@v7.27.0/internal/clientv2/multipart_test.go (about) 1 //go:build unit 2 // +build unit 3 4 package clientv2 5 6 import ( 7 "bytes" 8 "crypto/md5" 9 "io" 10 "math/rand" 11 "mime" 12 "mime/multipart" 13 "net/http" 14 "os" 15 "testing" 16 "time" 17 ) 18 19 func TestMultipart(t *testing.T) { 20 file1 := randFile(t, 1024*1024*10) 21 defer file1.Close() 22 file2 := randFile(t, 1024*1024*10) 23 defer file2.Close() 24 25 form := new(MultipartForm). 26 SetValue("test-1", "value-1"). 27 SetValue("test-2", "value-2"). 28 SetFile("test-file-1", "test-file-name-1", "application/json", file1). 29 SetFile("test-file-2", "test-file-name-2", "application/x-www-form-urlencoded", file2) 30 getRequestBody := GetMultipartFormRequestBody(form) 31 32 for i := 0; i < 5; i++ { 33 header := make(http.Header) 34 requestBody, err := getRequestBody(&RequestParams{Header: header}) 35 if err != nil { 36 t.Fatal(err) 37 } 38 reader, err := parseMultipart(requestBody, header) 39 if err != nil { 40 t.Fatal(err) 41 } 42 if part, err := reader.NextPart(); err != nil { 43 t.Fatal(err) 44 } else { 45 assertPartValue(t, part, "test-1", "value-1") 46 } 47 if part, err := reader.NextPart(); err != nil { 48 t.Fatal(err) 49 } else { 50 assertPartValue(t, part, "test-2", "value-2") 51 } 52 if part, err := reader.NextPart(); err != nil { 53 t.Fatal(err) 54 } else if f, err := os.Open(file1.Name()); err != nil { 55 t.Fatal(err) 56 } else { 57 assertPartFile(t, part, "test-file-1", "test-file-name-1", "application/json", f) 58 f.Close() 59 } 60 if part, err := reader.NextPart(); err != nil { 61 t.Fatal(err) 62 } else if f, err := os.Open(file2.Name()); err != nil { 63 t.Fatal(err) 64 } else { 65 assertPartFile(t, part, "test-file-2", "test-file-name-2", "application/x-www-form-urlencoded", f) 66 f.Close() 67 } 68 if _, err = reader.NextPart(); err != io.EOF { 69 t.Fatalf("unexpected error: %v", err) 70 } 71 } 72 } 73 74 func randFile(t *testing.T, n int64) *os.File { 75 file, err := os.CreateTemp("", "") 76 if err != nil { 77 t.Fatal(err) 78 } 79 _, err = io.CopyN(file, rand.New(rand.NewSource(time.Now().UnixNano())), 1024*1024*10) 80 if err != nil { 81 t.Fatal(err) 82 } 83 _, err = file.Seek(0, io.SeekStart) 84 if err != nil { 85 t.Fatal(err) 86 } 87 return file 88 } 89 90 func parseMultipart(r io.Reader, header http.Header) (*multipart.Reader, error) { 91 _, params, err := mime.ParseMediaType(header.Get("Content-Type")) 92 if err != nil { 93 return nil, err 94 } 95 return multipart.NewReader(r, params["boundary"]), nil 96 } 97 98 func assertPartValue(t *testing.T, part *multipart.Part, key, value string) { 99 if part.FormName() != key { 100 t.Fatalf("unexpected form name: %s != %s", part.FormName(), key) 101 } 102 buf := new(bytes.Buffer) 103 if _, err := io.Copy(buf, part); err != nil { 104 t.Fatal(err) 105 } 106 if buf.String() != value { 107 t.Fatalf("unexpected form value: %s != %s", buf.String(), value) 108 } 109 } 110 111 func assertPartFile(t *testing.T, part *multipart.Part, key, fileName, contentType string, value io.Reader) { 112 if part.FormName() != key { 113 t.Fatalf("unexpected form name: %s != %s", part.FormName(), key) 114 } 115 if part.FileName() != fileName { 116 t.Fatalf("unexpected file name: %s != %s", part.FileName(), fileName) 117 } 118 if acutalContentType := part.Header.Get("Content-Type"); acutalContentType != contentType { 119 t.Fatalf("unexpected content type: %s != %s", acutalContentType, contentType) 120 } 121 md5Hasher := md5.New() 122 if _, err := io.Copy(md5Hasher, part); err != nil { 123 t.Fatal(err) 124 } 125 actualMd5 := md5Hasher.Sum(nil) 126 md5Hasher.Reset() 127 if _, err := io.Copy(md5Hasher, value); err != nil { 128 t.Fatal(err) 129 } 130 expectedMd5 := md5Hasher.Sum(nil) 131 if !bytes.Equal(actualMd5, expectedMd5) { 132 t.Fatalf("unexpected form file value") 133 } 134 }