Skip to content

Commit

Permalink
Merge pull request #26 from wklken/208_enable_custom_sendfile_content…
Browse files Browse the repository at this point in the history
…-type

feat(sendfile): enable custom Content-Type for SendFile
  • Loading branch information
wklken authored Feb 19, 2022
2 parents b74826a + e87ba00 commit 87db5f5
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 2 deletions.
44 changes: 42 additions & 2 deletions gorequest.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"context"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
Expand Down Expand Up @@ -826,10 +827,11 @@ func (s *SuperAgent) SendString(content string) *SuperAgent {
type File struct {
Filename string
Fieldname string
MimeType string
Data []byte
}

// 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.
// SendFile function works only with type "multipart". The function accepts one mandatory and up to three optional arguments. The mandatory (first) argument is the file.
// The function accepts a path to a file as string:
//
// gorequest.New().
Expand Down Expand Up @@ -877,11 +879,32 @@ 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 a bool value skipFileNumbering. It defaults to "false",
// if fieldname is "file" and skipFileNumbering is set to "false", the fieldname will be automatically set to
// fileNUMBER, where number is the greatest existing number+1.
//
// b, _ := ioutil.ReadFile("./example_file.ext")
// gorequest.New().
// Post("http://example.com").
// Type("multipart").
// SendFile(b, "filename", "my_custom_fieldname", false).
// End()
//
// The fourth optional argument (fifth argument overall) is the mimetype request form-data part. It defaults to "application/octet-stream".
//
// b, _ := ioutil.ReadFile("./example_file.ext")
// gorequest.New().
// Post("http://example.com").
// Type("multipart").
// SendFile(b, "filename", "my_custom_fieldname", false, "mime_type").
// End()
//
func (s *SuperAgent) SendFile(file interface{}, args ...interface{}) *SuperAgent {

filename := ""
fieldname := "file"
skipFileNumbering := false
fileType := "application/octet-stream"

if len(args) >= 1 {
argFilename := fmt.Sprintf("%v", args[0])
Expand All @@ -904,6 +927,17 @@ func (s *SuperAgent) SendFile(file interface{}, args ...interface{}) *SuperAgent
}
}

if len(args) >= 4 {
argFileType := fmt.Sprintf("%v", args[3])
if len(argFileType) > 0 {
fileType = strings.TrimSpace(argFileType)
}
if fileType == "" {
s.Errors = append(s.Errors, errors.New("the fifth SendFile method argument for MIME type cannot be an empty string"))
return s
}
}

if (fieldname == "file" && !skipFileNumbering) || fieldname == "" {
fieldname = "file" + strconv.Itoa(len(s.FileData)+1)
}
Expand All @@ -926,6 +960,7 @@ func (s *SuperAgent) SendFile(file interface{}, args ...interface{}) *SuperAgent
s.FileData = append(s.FileData, File{
Filename: filename,
Fieldname: fieldname,
MimeType: fileType,
Data: data,
})
case reflect.Slice:
Expand All @@ -936,6 +971,7 @@ func (s *SuperAgent) SendFile(file interface{}, args ...interface{}) *SuperAgent
f := File{
Filename: filename,
Fieldname: fieldname,
MimeType: fileType,
Data: make([]byte, len(slice)),
}
for i := range slice {
Expand All @@ -952,6 +988,9 @@ func (s *SuperAgent) SendFile(file interface{}, args ...interface{}) *SuperAgent
if len(args) == 3 {
return s.SendFile(v.Elem().Interface(), args[0], args[1], args[2])
}
if len(args) == 4 {
return s.SendFile(v.Elem().Interface(), args[0], args[1], args[2], args[3])
}
return s.SendFile(v.Elem().Interface())
default:
if v.Type() == reflect.TypeOf(os.File{}) {
Expand All @@ -967,6 +1006,7 @@ func (s *SuperAgent) SendFile(file interface{}, args ...interface{}) *SuperAgent
s.FileData = append(s.FileData, File{
Filename: filename,
Fieldname: fieldname,
MimeType: fileType,
Data: data,
})
return s
Expand Down Expand Up @@ -1359,7 +1399,7 @@ 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, _ := CreateFormFile(mw, file.Fieldname, file.Filename, file.MimeType)
fw.Write(file.Data)
}
contentReader = buf
Expand Down
27 changes: 27 additions & 0 deletions gorequest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1203,13 +1203,15 @@ func TestMultipartRequest(t *testing.T) {
const case10b_send_file_by_path_pointer = "/send_file_by_path_pointer"
const case11_send_file_by_path_without_name = "/send_file_by_path_without_name"
const case12_send_file_by_path_without_name_but_with_fieldname = "/send_file_by_path_without_name_but_with_fieldname"
const case121_send_file_by_path_with_name_and_fieldname_and_mimetype = "/send_file_by_path_with_name_and_fieldname_and_mimetype"

const case13_send_file_by_content_without_name = "/send_file_by_content_without_name"
const case13a_send_file_by_content_without_name_pointer = "/send_file_by_content_without_name_pointer"
const case14_send_file_by_content_with_name = "/send_file_by_content_with_name"

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 case161_send_file_by_content_with_name_and_fieldname_and_mimetype = "/send_file_by_content_with_name_and_fieldname_and_mimetype"

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 @@ -1441,6 +1443,21 @@ func TestMultipartRequest(t *testing.T) {
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 case161_send_file_by_content_with_name_and_fieldname_and_mimetype, case121_send_file_by_path_with_name_and_fieldname_and_mimetype:
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 != "MY_LICENSE" {
t.Error("Expected Filename:MY_LICENSE", "| but got", r.MultipartForm.File["my_fieldname"][0].Filename)
}
if r.MultipartForm.File["my_fieldname"][0].Header["Content-Type"][0] != "application/json" {
t.Error("Expected Header:Content-Type:application/json", "| 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 {
t.Error("Expected length of files:[] == 2", "| but got", len(r.MultipartForm.File))
Expand Down Expand Up @@ -1661,6 +1678,11 @@ func TestMultipartRequest(t *testing.T) {
SendFile(fileByPath, "", "my_fieldname").
End()

New().Post(ts.URL+case121_send_file_by_path_with_name_and_fieldname_and_mimetype).
Type("multipart").
SendFile(fileByPath, "MY_LICENSE", "my_fieldname", false, "application/json").
End()

b, _ := ioutil.ReadFile("./LICENSE")
New().Post(ts.URL + case13_send_file_by_content_without_name).
Type("multipart").
Expand All @@ -1687,6 +1709,11 @@ func TestMultipartRequest(t *testing.T) {
SendFile(b, "MY_LICENSE", "my_fieldname").
End()

New().Post(ts.URL+case161_send_file_by_content_with_name_and_fieldname_and_mimetype).
Type("multipart").
SendFile(b, "MY_LICENSE", "my_fieldname", false, "application/json").
End()

New().Post(ts.URL + case17_send_file_multiple_by_path_and_content_without_name).
Type("multipart").
SendFile("./LICENSE").
Expand Down
22 changes: 22 additions & 0 deletions util.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package gorequest

import (
"fmt"
"io"
"mime/multipart"
"net/http"
"net/textproto"
"strings"
"unsafe"
)

Expand Down Expand Up @@ -104,3 +109,20 @@ func filterFlags(content string) string {
}
return content
}

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

func escapeQuotes(s string) string {
return quoteEscaper.Replace(s)
}

// CreateFormFile is a convenience wrapper around CreatePart. It creates
// a new form-data header with the provided field name and file name.
func CreateFormFile(w *multipart.Writer, fieldname, filename string, contenttype string) (io.Writer, error) {
h := make(textproto.MIMEHeader)
h.Set("Content-Disposition",
fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
escapeQuotes(fieldname), escapeQuotes(filename)))
h.Set("Content-Type", contenttype)
return w.CreatePart(h)
}

0 comments on commit 87db5f5

Please sign in to comment.