【问题标题】:Sending Multipart request with curl usign GoLang使用 GoLang 通过 curl 发送 Multipart 请求
【发布时间】:2018-06-16 01:17:53
【问题描述】:

我想把下面的代码转换成golang。

curl -X POST
  'https://api.documo.com/v1/fax/send' \
  -H 'Authorization: Basic YOUR_API_KEY' \
  -H 'content-type: multipart/form-data' \
  -F 'recipientFax=12345678900' \
  -F 'coverPage=false' \
  -F 'recipientName=John' \
  -F 'subject=test' \
  -F 'notes=test' \
  -F '=@/home/user/Documents/Sample.pdf'

请帮忙

以下是我尝试过的代码。

func SendFaxDocumo(files []*multipart.FileHeader, params map[string]string) {

    request, err := SendMultipartRequest("https://api.documo.com/v1/fax/send", params, files)
    if err != nil {
        fmt.Println(err.Error())

    }
    client := &http.Client{}
    resp, err := client.Do(request)
    if err != nil {
        fmt.Println(err.Error())
    } else {
        body := &bytes.Buffer{}
        _, err := body.ReadFrom(resp.Body)
        if err != nil {
            fmt.Println(err.Error())
        }
        resp.Body.Close()
        fmt.Println(resp.StatusCode)
        fmt.Println(resp.Header)
        fmt.Println(body)
    }
}

func SendMultipartRequest(uri string, params map[string]string, files []*multipart.FileHeader) (*http.Request, error) {

    body := &bytes.Buffer{}
    writer := multipart.NewWriter(body)

    for i, _ := range files {
        // string_index := strconv.Itoa(i)
        file, err := files[i].Open()
        defer file.Close()
        if err != nil {
            fmt.Println(err)
        }

        doc , err := ioutil.ReadAll(file)
        if err != nil {
            fmt.Println(err,980)
        }

        part, err := writer.CreateFormFile("file",files[i].Filename)

        if err != nil {
            fmt.Println(err)
        }

        part.Write([]byte(string(doc)))
        if _, err = io.Copy(part, file); err != nil {
            fmt.Println(err)
        }

        if err != nil {
            fmt.Println(err)
        }
    }

    for key, val := range params {
        _ = writer.WriteField(key, val)
    }

    err := writer.Close()
    if err != nil {
        return nil, err
    }

    req, err := http.NewRequest("POST", uri, body)
    req.Header.Set("Content-Type", writer.FormDataContentType())
    req.Header.Set("Authorization", "Basic my_key")

    return req, err
}

我得到的错误如下: {"error":{"name":"Error","message":"文件扩展名不允许。允许的格式:tiff,gif,png,jpeg,pdf,doc,xls,ppt,pcl,eps,ps,txt ,rtf,xml,fxc,docx,pptx,xlsx"}}

这是从请求的 url 得到的响应,即使我正在通过 pdf 尝试它。

【问题讨论】:

标签: curl go multipartform-data


【解决方案1】:

我只是花了很长时间研究同样的问题。在使用httputil.DumpRequest 查看我实际传递给Documo 的数据后,我看到文件字段的内容类型标头设置为Content-Type: application/octet-stream。这是由您使用的CreateFormFile 方法设置的。源代码见multipart pkg

octet-stream 子类型用作二进制文件或需要在应用程序中打开的文件的默认值。请参阅MDNIANAthis awesome explanation

据我了解,问题在于 Documo 希望我们明确告诉他们我们要向他们发送什么类型的文件,以便他们知道如何处理渲染。所以就我而言,octet-stream 没有告诉他们任何事情。将文件内容类型切换为application/pdf 就可以了。

解决方案

我基本上没有调用writer.CreateFormField,而是从CreateFormFile的源代码中复制了三行并将Content-Type的标题硬编码为application/pdf而不是application/octet-stream(注意:我的服务只处理现在是 PDF,因此如果您的文件类型不同,您可能需要调整它)。

代码:


func SendMultipartRequest(uri string, params map[string]string, files []*multipart.FileHeader) (*http.Request, error) {

    body := &bytes.Buffer{}
    writer := multipart.NewWriter(body)

    for i, _ := range files {
        file, err := files[i].Open()
        defer file.Close()
        if err != nil {
            fmt.Println(err)
        }

        //////////////////////////////////////////////////////
        // replace create CreateFormFile with this
        h := make(textproto.MIMEHeader) // import "net/textproto"
        h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="file"; filename="%s"`, files[i].Filename)
        h.Set("Content-Type", "application/pdf") // this is where you'll set your document type(s)
        fileWriter, err := form.CreatePart(h)
        ///////////////////////////////////////////////////////

        if err != nil {
            fmt.Println(err)
        }

        if _, err = io.Copy(fileWriter, file); err != nil {
            fmt.Println(err)
        }

        if err != nil {
            fmt.Println(err)
        }
    }

    for key, val := range params {
        _ = writer.WriteField(key, val)
    }

    err := writer.Close()
    if err != nil {
        return nil, err
    }

    req, err := http.NewRequest("POST", uri, body)
    req.Header.Set("Content-Type", writer.FormDataContentType())
    req.Header.Set("Authorization", "Basic my_key")

    return req, err
}

这不是最性感的解决方案,但我现在终于可以通过 Documo 发送完整的文件了。

希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-06-21
    • 2017-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-28
    • 1970-01-01
    相关资源
    最近更新 更多