I'm using a request bin (running on localhost:8080/anything) to capture what my application sends (kennethreitz/httpbin)
so now i have two similar POST requests using two different user agents, which submits a image file and two other fields.
1.CURL based
curl -X POST 'http://localhost:8080/anything' \
-H 'Authorization: Bearer xyz' \
-F 'file=@"wikipedia_logo.png"' \
-F 'type="image/png"' \
-F 'messaging_product="whatsapp"'
which results in following data on requestbin
{
"args": {},
"data": "",
"files": {
// take note of this file details
"file": "data:image/png;base64,<base64-encoded-file>"
},
"form": {
"messaging_product": "whatsapp",
"type": "image/png"
},
"headers": {
"Accept": "*/*",
"Authorization": "Bearer xyz",
"Content-Length": "27693",
"Content-Type": "multipart/form-data; boundary=------------------------94b30407c7fea5c0",
"Host": "localhost:8080",
"User-Agent": "curl/7.81.0"
},
"json": null,
"method": "POST",
"origin": "172.17.0.1",
"url": "http://localhost:8080/anything"
}
2.go-http
func main() {
url := "http://localhost:8080/anything"
filePath := "wikipedia_logo.png"
accessToken := "xyz"
file, err := os.Open(filePath)
if err != nil {
panic(err)
}
defer file.Close()
fileBytes, err := io.ReadAll(file)
if err != nil {
panic(err)
}
var requestBody bytes.Buffer
writer := multipart.NewWriter(&requestBody)
part, err := writer.CreateFormFile("file", filePath)
if err != nil {
panic(err)
}
_, err = part.Write(fileBytes)
if err != nil {
panic(err)
}
err = writer.WriteField("type", "image/png")
if err != nil {
panic(err)
}
err = writer.WriteField("messaging_product", "whatsapp")
if err != nil {
panic(err)
}
err = writer.Close()
if err != nil {
panic(err)
}
req, err := http.NewRequest("POST", url, &requestBody)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", accessToken))
req.Header.Set("Content-Type", writer.FormDataContentType())
// Make the request
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
b, err := io.ReadAll(resp.Body)
if err != nil {
panic(err)
}
fmt.Println(string(b))
}
which results in
{
"args": {},
"data": "",
"files": {
// why the file type is data:application/octet-stream
"file": "data:application/octet-stream;base64,<bas64-encode-image>"
},
"form": {
"messaging_product": "whatsapp",
"type": "image/png"
},
"headers": {
"Accept-Encoding": "gzip",
"Authorization": "Bearer xyz",
"Content-Length": "27788",
"Content-Type": "multipart/form-data; boundary=caaba02d7dcf3f25c63b0c98466a80518331e4e055d2cd43a72780760ee7",
"Host": "localhost:8080",
"User-Agent": "Go-http-client/1.1"
},
"json": null,
"method": "POST",
"origin": "172.17.0.1",
"url": "http://localhost:8080/anything"
}
Why the file media type is not image/png in the go-http user agent, how to set it?
It is how CreateFormFile
works:
h.Set("Content-Type", "application/octet-stream")
You can create a general version:
// CreateFormFileExt is a convenience wrapper around CreatePart. It creates
// a new form-data header with the provided field name and file name and MIME type.
func CreateFormFileExt(w *multipart.Writer, fieldname, filename, mimeType 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", mimeType)
return w.CreatePart(h)
}
var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"")
func escapeQuotes(s string) string {
return quoteEscaper.Replace(s)
}