Anam Shah
Anam Shah

Reputation: 319

Sending Multipart request with curl usign GoLang

I want to convert below code to 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'

Please help

Below is my code what i have tried.

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
}

And the error i am getting is below: {"error":{"name":"Error","message":"File extension not allowed. Allowed formats: tiff,gif,png,jpeg,pdf,doc,xls,ppt,pcl,eps,ps,txt,rtf,xml,fxc,docx,pptx,xlsx"}}

This is the response got from the url requested even if i am trying it by pdf.

Upvotes: 1

Views: 1948

Answers (1)

Matt
Matt

Reputation: 11

I just spent a long few days researching the same problem. After using httputil.DumpRequest to view what data I was actually passing to Documo, I saw that the content-type header for the file field was set to Content-Type: application/octet-stream. This is set by the CreateFormFile method you're using. See the multipart pkg for the source code.

The octet-stream subtype is used as a default for binary files or files that need to be opened in an application. See MDN, IANA, or this awesome explanation.

The problem, as far as I understand it, is that Documo expects us to explicitly tell them what type of file we're sending them so they know how to handle the rendering. So for my case, octet-stream didn't tell them anything. Switching the file content-type to application/pdf did the trick.

Solution

Instead of calling writer.CreateFormField, I basically copied the three lines from the source code of CreateFormFile and hard coded the Content-Type to header to application/pdf instead of application/octet-stream (Note: my service only deals with PDF right now, so you may need to adjust this if your files are different types).

Code:


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
}

It's not the sexiest solution but I'm finally able to send complete documents through Documo now.

Hope this helps.

Upvotes: 1

Related Questions