可以使用 Content-Type: multipart/form-data 发布吗?

huangapple go评论111阅读模式
英文:

Can I post with Content-Type: multipart/form-data

问题

如何使用Content-Type: multipart/form-data、[]byte参数和字符串参数向API进行POST请求?我已经尝试过,但是失败了。

错误信息:

  1. details: "[301 301 Moved Permanently]<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n<html>\r\n301 Moved Permanently\r\n<body bgcolor=\"white\">\r\n301 Moved Permanently\r\n<p>The requested resource has been assigned a new permanent URI.</p >\r\n<hr/>Powered by Tengine/2.1.0</body>\r\n</html>\r\n"

Go代码:

  1. func NewPost2(url string) ([]byte, error) {
  2. m := make(map[string]interface{}, 0)
  3. m["fileName"] = "good"
  4. m["name"] = Base64ToByte("/9j/4AAQSkZJRgABAQEAeAB4AAD/2wBDAAIBAQIBAQICAgICAgICAwUDAwMDAwYEBAMFBwYHBwcGBwcICQsJCAgKCAcHCg0KCgsMDAwMBwkODw0MDgsMDAz/2wBDAQICAgMDAwYDAwYMCAcIDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAz/wAARCAABAAEDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDHooor+wD+Zz//2Q==")
  5. b, _ := json.Marshal(m)
  6. httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(b))
  7. httpReq.Header.Set("Content-Type", "multipart/form-data;charset=UTF-8")
  8. client := &http.Client{}
  9. resp, err := client.Do(httpReq)
  10. if err != nil {
  11. return nil, err
  12. }
  13. defer resp.Body.Close()
  14. if resp.StatusCode < 200 || resp.StatusCode >= 300 {
  15. b, _ := ioutil.ReadAll(resp.Body)
  16. return nil, fmt.Errorf("[%d %s]%s", resp.StatusCode, resp.Status, string(b))
  17. }
  18. respData, err := ioutil.ReadAll(resp.Body)
  19. if err != nil {
  20. return nil, err
  21. }
  22. return respData, nil
  23. }
英文:

How do I POST to an API with Content-Type: multipart/form-data, []byte parameters and string arguments? I have tried, but it is failing.

Error message:

  1. details: &quot;[301 301 Moved Permanently]&lt;!DOCTYPE HTML PUBLIC \&quot;-//IETF//DTD HTML 2.0//EN\&quot;&gt;\r\n&lt;html&gt;\r\n301 Moved Permanently\r\n&lt;body bgcolor=\&quot;white\&quot;&gt;\r\n301 Moved Permanently\r\n&lt;p&gt;The requested resource has been assigned a new permanent URI.&lt;/p &gt;\r\n&lt;hr/&gt;Powered by Tengine/2.1.0&lt;/body&gt;\r\n&lt;/html&gt;\r\n&quot;

Go code:

  1. func NewPost2(url string) ([]byte, error) {
  2. m := make(map[string]interface{}, 0)
  3. m[&quot;fileName&quot;] =&quot;good&quot;
  4. m[&quot;name&quot;] = Base64ToByte(&quot;/9j/4AAQSkZJRgABAQEAeAB4AAD/2wBDAAIBAQIBAQICAgICAgICAwUDAwMDAwYEBAMFBwYHBwcGBwcICQsJCAgKCAcHCg0KCgsMDAwMBwkODw0MDgsMDAz/2wBDAQICAgMDAwYDAwYMCAcIDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAz/wAARCAABAAEDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDHooor+wD+Zz//2Q==&quot;)
  5. b, _ := json.Marshal(m)
  6. httpReq, err := http.NewRequest(&quot;POST&quot;, url, bytes.NewBuffer(b))
  7. httpReq.Header.Set(&quot;Content-Type&quot;, &quot;multipart/form-data;charset=UTF-8&quot;)
  8. client := &amp;http.Client{}
  9. resp, err := client.Do(httpReq)
  10. if err != nil {
  11. return nil, err
  12. }
  13. defer resp.Body.Close()
  14. if resp.StatusCode &lt; 200 || resp.StatusCode &gt;= 300 {
  15. b, _ := ioutil.ReadAll(resp.Body)
  16. return nil, fmt.Errorf(&quot;[%d %s]%s&quot;, resp.StatusCode, resp.Status, string(b))
  17. }
  18. respData, err := ioutil.ReadAll(resp.Body)
  19. if err != nil {
  20. return nil, err
  21. }
  22. return respData, nil
  23. }

答案1

得分: 10

现在,我非常高兴地分享我的解决方案。

  1. func NewPostFile(url string, paramTexts map[string]interface{}, paramFile FileItem) ([]byte, error) {
  2. // if paramFiles ==nil {
  3. // return NewPost(url,paramTexts,header,transport)
  4. // }
  5. bodyBuf := &bytes.Buffer{}
  6. bodyWriter := multipart.NewWriter(bodyBuf)
  7. for k, v := range paramTexts {
  8. bodyWriter.WriteField(k, v.(string))
  9. }
  10. fileWriter, err := bodyWriter.CreateFormFile(paramFile.Key, paramFile.FileName)
  11. if err != nil {
  12. fmt.Println(err)
  13. //fmt.Println("Create form file error: ", error)
  14. return nil, err
  15. }
  16. fileWriter.Write(paramFile.Content)
  17. contentType := bodyWriter.FormDataContentType()
  18. bodyWriter.Close()
  19. fmt.Println(bodyBuf.String())
  20. resp, err := http.Post(url, contentType, bodyBuf)
  21. if err != nil {
  22. return nil, err
  23. }
  24. defer resp.Body.Close()
  25. fmt.Println(resp)
  26. if resp.StatusCode < 200 || resp.StatusCode >= 300 {
  27. b, _ := ioutil.ReadAll(resp.Body)
  28. return nil, fmt.Errorf("[%d %s]%s", resp.StatusCode, resp.Status, string(b))
  29. }
  30. respData, err := ioutil.ReadAll(resp.Body)
  31. if err != nil {
  32. return nil, err
  33. }
  34. fmt.Println(string(respData))
  35. return respData, nil
  36. }
  37. type FileItem struct {
  38. Key string //image_content
  39. FileName string //test.jpg
  40. Content []byte //[]byte
  41. }

以上是要翻译的内容。

英文:

Now, I am very happy with the mood to share my solution

  1. func NewPostFile(url string, paramTexts map[string]interface{}, paramFile FileItem) ([]byte, error) {
  2. // if paramFiles ==nil {
  3. // return NewPost(url,paramTexts,header,transport)
  4. // }
  5. bodyBuf := &amp;bytes.Buffer{}
  6. bodyWriter := multipart.NewWriter(bodyBuf)
  7. for k, v := range paramTexts {
  8. bodyWriter.WriteField(k, v.(string))
  9. }
  10. fileWriter, err := bodyWriter.CreateFormFile(paramFile.Key, paramFile.FileName)
  11. if err != nil {
  12. fmt.Println(err)
  13. //fmt.Println(&quot;Create form file error: &quot;, error)
  14. return nil, err
  15. }
  16. fileWriter.Write(paramFile.Content)
  17. contentType := bodyWriter.FormDataContentType()
  18. bodyWriter.Close()
  19. fmt.Println(bodyBuf.String())
  20. resp, err := http.Post(url, contentType, bodyBuf)
  21. if err != nil {
  22. return nil, err
  23. }
  24. defer resp.Body.Close()
  25. fmt.Println(resp)
  26. if resp.StatusCode &lt; 200 || resp.StatusCode &gt;= 300 {
  27. b, _ := ioutil.ReadAll(resp.Body)
  28. return nil, fmt.Errorf(&quot;[%d %s]%s&quot;, resp.StatusCode, resp.Status, string(b))
  29. }
  30. respData, err := ioutil.ReadAll(resp.Body)
  31. if err != nil {
  32. return nil, err
  33. }
  34. fmt.Println(string(respData))
  35. return respData, nil

}

  1. type FileItem struct {
  2. Key string //image_content
  3. FileName string //test.jpg
  4. Content []byte //[]byte

}

答案2

得分: 1

我将多部分代码封装在一个函数中,因为在发出请求之前需要使用Close关闭它。我的方法使用@作为启发式方法,类似于cURL [1]:

  1. package main
  2. import (
  3. "bytes"
  4. "io"
  5. "mime/multipart"
  6. "os"
  7. "strings"
  8. )
  9. func createForm(form map[string]string) (string, io.Reader, error) {
  10. body := new(bytes.Buffer)
  11. mp := multipart.NewWriter(body)
  12. defer mp.Close()
  13. for key, val := range form {
  14. if strings.HasPrefix(val, "@") {
  15. val = val[1:]
  16. file, err := os.Open(val)
  17. if err != nil { return "", nil, err }
  18. defer file.Close()
  19. part, err := mp.CreateFormFile(key, val)
  20. if err != nil { return "", nil, err }
  21. io.Copy(part, file)
  22. } else {
  23. mp.WriteField(key, val)
  24. }
  25. }
  26. return mp.FormDataContentType(), body, nil
  27. }

示例:

  1. package main
  2. import "net/http"
  3. func main() {
  4. form := map[string]string{"profile": "@portrait.jpg", "name": "John"}
  5. ct, body, err := createForm(form)
  6. if err != nil {
  7. panic(err)
  8. }
  9. http.Post("https://stackoverflow.com", ct, body)
  10. }
  1. https://curl.se/docs/manpage.html#-F
英文:

I wrapped the multipart code in a function, as you need to Close it before you
can make a request. Also my method is using @ as a heuristic, similar to
cURL [1]:

  1. package main
  2. import (
  3. &quot;bytes&quot;
  4. &quot;io&quot;
  5. &quot;mime/multipart&quot;
  6. &quot;os&quot;
  7. &quot;strings&quot;
  8. )
  9. func createForm(form map[string]string) (string, io.Reader, error) {
  10. body := new(bytes.Buffer)
  11. mp := multipart.NewWriter(body)
  12. defer mp.Close()
  13. for key, val := range form {
  14. if strings.HasPrefix(val, &quot;@&quot;) {
  15. val = val[1:]
  16. file, err := os.Open(val)
  17. if err != nil { return &quot;&quot;, nil, err }
  18. defer file.Close()
  19. part, err := mp.CreateFormFile(key, val)
  20. if err != nil { return &quot;&quot;, nil, err }
  21. io.Copy(part, file)
  22. } else {
  23. mp.WriteField(key, val)
  24. }
  25. }
  26. return mp.FormDataContentType(), body, nil
  27. }

Example:

  1. package main
  2. import &quot;net/http&quot;
  3. func main() {
  4. form := map[string]string{&quot;profile&quot;: &quot;@portrait.jpg&quot;, &quot;name&quot;: &quot;John&quot;}
  5. ct, body, err := createForm(form)
  6. if err != nil {
  7. panic(err)
  8. }
  9. http.Post(&quot;https://stackoverflow.com&quot;, ct, body)
  10. }
  1. https://curl.se/docs/manpage.html#-F

答案3

得分: 0

在301响应中,新的URL在响应的headers中指定,而不是在响应的主体中。

(参见https://en.wikipedia.org/wiki/HTTP_301)

尝试打印:

  1. resp.Header["Location"]

如果您将此错误作为最终响应,这也意味着http.Client选择不遵循此重定向。

文档中指出,Client的默认策略是遵循最多10次重定向。

为了调试重定向,您可以编写自己的CheckRedirect函数,该函数可以打印URL序列,例如:

英文:

On a 301 response, the new url is specified in the headers of the response, not its body

(see https://en.wikipedia.org/wiki/HTTP_301)

try printing :

  1. resp.Header[&quot;Location&quot;]

If you have this error as a final response, this also means that the http.Client chose to not follow this redirection.

The doc says that the dafult policy for a Client is to follow up to 10 redirects.

In order to debug redirects, you can write your own CheckRedirect function, which can for instance print the sequence of urls

huangapple
  • 本文由 发表于 2017年6月1日 16:30:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/44302374.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定