Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add optional contenttype to multipart/form-data request (revised) #170

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 55 additions & 16 deletions gorequest.go
Original file line number Diff line number Diff line change
Expand Up @@ -732,9 +732,10 @@ func (s *SuperAgent) SendString(content string) *SuperAgent {
}

type File struct {
Filename string
Fieldname string
Data []byte
Filename string
Fieldname string
Data []byte
ContentType string
}

// SendFile function works only with type "multipart". The function accepts one mandatory and up to two optional arguments. The mandatory (first) argument is the file.
Expand Down Expand Up @@ -784,17 +785,31 @@ type File struct {
// SendFile(b, "", "my_custom_fieldname"). // filename left blank, will become "example_file.ext"
// End()
//
// The third optional argument (fourth argument overall) is the contenttype in the multipart/form-data request. It defaults to "application/octet-stream"
//
// b, _ := ioutil.ReadFile("./example_file.ext")
// gorequest.New().
// Post("http://example.com").
// Type("multipart").
// SendFile(b, "", "my_custom_fieldname", "application/xml").
// End()
//
func (s *SuperAgent) SendFile(file interface{}, args ...string) *SuperAgent {

filename := ""
fieldname := "file"
contenttype := ""

if len(args) >= 1 && len(args[0]) > 0 {
filename = strings.TrimSpace(args[0])
}
if len(args) >= 2 && len(args[1]) > 0 {
fieldname = strings.TrimSpace(args[1])
}
if len(args) >= 3 && len(args[2]) > 0 {
contenttype = strings.TrimSpace(args[2])
}

if fieldname == "file" || fieldname == "" {
fieldname = "file" + strconv.Itoa(len(s.FileData)+1)
}
Expand All @@ -815,19 +830,21 @@ func (s *SuperAgent) SendFile(file interface{}, args ...string) *SuperAgent {
return s
}
s.FileData = append(s.FileData, File{
Filename: filename,
Fieldname: fieldname,
Data: data,
Filename: filename,
Fieldname: fieldname,
Data: data,
ContentType: contenttype,
})
case reflect.Slice:
slice := makeSliceOfReflectValue(v)
if filename == "" {
if filename == "" && contenttype == "" { //default only if contenttype not specified
filename = "filename"
}
f := File{
Filename: filename,
Fieldname: fieldname,
Data: make([]byte, len(slice)),
Filename: filename,
Fieldname: fieldname,
Data: make([]byte, len(slice)),
ContentType: contenttype,
}
for i := range slice {
f.Data[i] = slice[i].(byte)
Expand All @@ -837,9 +854,12 @@ func (s *SuperAgent) SendFile(file interface{}, args ...string) *SuperAgent {
if len(args) == 1 {
return s.SendFile(v.Elem().Interface(), args[0])
}
if len(args) >= 2 {
if len(args) == 2 {
return s.SendFile(v.Elem().Interface(), args[0], args[1])
}
if len(args) >= 3 {
return s.SendFile(v.Elem().Interface(), args[0], args[1], args[2])
}
return s.SendFile(v.Elem().Interface())
default:
if v.Type() == reflect.TypeOf(os.File{}) {
Expand All @@ -853,9 +873,10 @@ func (s *SuperAgent) SendFile(file interface{}, args ...string) *SuperAgent {
return s
}
s.FileData = append(s.FileData, File{
Filename: filename,
Fieldname: fieldname,
Data: data,
Filename: filename,
Fieldname: fieldname,
Data: data,
ContentType: contenttype,
})
return s
}
Expand Down Expand Up @@ -1237,8 +1258,20 @@ func (s *SuperAgent) MakeRequest() (*http.Request, error) {
// add the files
if len(s.FileData) != 0 {
for _, file := range s.FileData {
fw, _ := mw.CreateFormFile(file.Fieldname, file.Filename)
fw.Write(file.Data)
if len(file.ContentType) > 0 {
h := make(textproto.MIMEHeader)
h.Set("Content-Type", file.ContentType)
var fns string
if len(file.Filename) > 0 {
fns = fmt.Sprintf(`; filename="%s"`, escapeQuotes(file.Filename))
}
h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"%s`, escapeQuotes(file.Fieldname), fns))
fw, _ := mw.CreatePart(h)
fw.Write(file.Data)
} else {
fw, _ := mw.CreateFormFile(file.Fieldname, file.Filename)
fw.Write(file.Data)
}
}
contentReader = buf
}
Expand Down Expand Up @@ -1310,3 +1343,9 @@ func (s *SuperAgent) AsCurlCommand() (string, error) {
}
return cmd.String(), nil
}

var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")

func escapeQuotes(s string) string {
return quoteEscaper.Replace(s)
}
24 changes: 24 additions & 0 deletions gorequest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,7 @@ func TestMultipartRequest(t *testing.T) {

const case15_send_file_by_content_without_name_but_with_fieldname = "/send_file_by_content_without_name_but_with_fieldname"
const case16_send_file_by_content_with_name_and_with_fieldname = "/send_file_by_content_with_name_and_with_fieldname"
const case16a_send_file_by_content_with_name_fieldname_and_content_type = "/send_file_by_content_with_name_fieldname_and_content_type"

const case17_send_file_multiple_by_path_and_content_without_name = "/send_file_multiple_by_path_and_content_without_name"
const case18_send_file_multiple_by_path_and_content_with_name = "/send_file_multiple_by_path_and_content_with_name"
Expand Down Expand Up @@ -997,6 +998,24 @@ func TestMultipartRequest(t *testing.T) {
if r.MultipartForm.File["my_fieldname"][0].Header["Content-Type"][0] != "application/octet-stream" {
t.Error("Expected Header:Content-Type:application/octet-stream", "| but got", r.MultipartForm.File["my_fieldname"][0].Header["Content-Type"])
}
checkFile(t, r.MultipartForm.File["my_fieldname"][0])
case case16a_send_file_by_content_with_name_fieldname_and_content_type:

if len(r.MultipartForm.File) != 1 {
t.Error("Expected length of files:[] == 1", "| but got", len(r.MultipartForm.File))
}
if _, ok := r.MultipartForm.File["my_fieldname"]; !ok {
keys := reflect.ValueOf(r.MultipartForm.File).MapKeys()
t.Error("Expected Fieldname:my_fieldname", "| but got", keys)
}
if r.MultipartForm.File["my_fieldname"][0].Filename != "LICENSE" {
t.Error("Expected Filename:LICENSE", "| but got", r.MultipartForm.File["my_fieldname"][0].Filename)
}

if r.MultipartForm.File["my_fieldname"][0].Header["Content-Type"][0] != "application/xml" {
t.Error("Expected Header:Content-Type:application/xml", "| but got", r.MultipartForm.File["my_fieldname"][0].Header["Content-Type"])
}

checkFile(t, r.MultipartForm.File["my_fieldname"][0])
case case17_send_file_multiple_by_path_and_content_without_name:
if len(r.MultipartForm.File) != 2 {
Expand Down Expand Up @@ -1234,6 +1253,11 @@ func TestMultipartRequest(t *testing.T) {
SendFile(b, "MY_LICENSE", "my_fieldname").
End()

New().Post(ts.URL+case16a_send_file_by_content_with_name_fieldname_and_content_type).
Type("multipart").
SendFile(b, "LICENSE", "my_fieldname", "application/xml").
End()

New().Post(ts.URL + case17_send_file_multiple_by_path_and_content_without_name).
Type("multipart").
SendFile("./LICENSE").
Expand Down