1

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.

Flimzy
  • 60,850
  • 13
  • 104
  • 147
Anam Shah
  • 319
  • 1
  • 9
  • 2
    You don't seem to have tried anything. Please show what you have attempted and what did not work. Please include a [Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve). – Marc Jan 06 '18 at 11:45
  • @Marc i have added what you are asking, please check – Anam Shah Jan 06 '18 at 12:12
  • Instead of how you send the request we are more interested in how you genreate the request in `SendMultipartRequest`. – leaf bebop Jan 06 '18 at 14:03
  • Possible duplicate of [POST data using the Content-Type multipart/form-data](https://stackoverflow.com/questions/20205796/post-data-using-the-content-type-multipart-form-data) – 030 Apr 27 '19 at 08:28

1 Answers1

1

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.

Matt
  • 11
  • 3