0

I'm following this official Docs: https://developers.facebook.com/docs/messenger-platform/reference/attachment-upload-api/ to try to send a message with video attachment. What I've tried:

//...
    api.BaseRoutes.Conversation.Handle("/video", api.ApiSessionRequired(sendVideo)).Methods("POST")

//...

func sendVideo(c *Context, w http.ResponseWriter, r *http.Request) {
    c.RequirePageId().RequireConversationId()
    if c.Err != nil {
        return
    }

    conversation, getErr := c.App.GetConversation(c.Params.ConversationId)
    if getErr != nil {
        c.Err = getErr
        return
    }

    if err := r.ParseMultipartForm(25*1024*1024); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    // detect file header
    f, _, err := r.FormFile("files")

    // Create a buffer to store the header of the file in
    fileHeader := make([]byte, 512)

    // Copy the headers into the FileHeader buffer
    if _, err := f.Read(fileHeader); err != nil {

    }

    // set position back to start.
    if _, err := f.Seek(0, 0); err != nil {

    }

    _fileType := http.DetectContentType(fileHeader)

    m := r.MultipartForm

    fileArray, ok := m.File["files"]
    if !ok {
        c.Err = model.NewAppError("uploadPlugin", "api.plugin.upload.no_file.app_error", nil, "", http.StatusBadRequest)
        return
    }

    if len(fileArray) <= 0 {
        c.Err = model.NewAppError("uploadPlugin", "api.plugin.upload.array.app_error", nil, "", http.StatusBadRequest)
        return
    }

    file, err := fileArray[0].Open()

    if err != nil {
        c.Err = model.NewAppError("uploadPlugin", "api.plugin.upload.file.app_error", nil, "", http.StatusBadRequest)
        return
    }
    defer file.Close()

    // build a form body
    body := &bytes.Buffer{}
    writer := multipart.NewWriter(body)

    // add form fields
    writer.WriteField("message", "{\"attachment\":{\"type\":\"video\", \"payload\":{\"is_reusable\":true}}}\")

    //fileWriter, err := CreateFormFile(writer, "filedata", fileArray[0].Filename)

    // add a form file to the body
    fileWriter, err := writer.CreateFormFile("filedata", fileArray[0].Filename)
    if err != nil {
        c.Err = model.NewAppError("upload_video", "upload_video.error", nil, "", http.StatusBadRequest)
        return
    }

    // copy the file into the fileWriter
    _, err = io.Copy(fileWriter, file)
    if err != nil {
        c.Err = model.NewAppError("upload_video", "upload_video.error", nil, "", http.StatusBadRequest)
        return
    }

    // Close the body writer
    writer.Close()

    reqUrl := "https://graph.facebook.com/v10.0/me/message_attachments"
    token := c.App.Session().GetPageToken(c.Params.PageId)
    reqUrl += "?access_token=" + token

    var netTransport = &http.Transport{
        Dial: (&net.Dialer{
            Timeout: 120 * time.Second,
        }).Dial,
        TLSHandshakeTimeout:   120 * time.Second,
        ResponseHeaderTimeout: 120 * time.Second, // This will fixed the i/o timeout error
    }

    client := &http.Client{
        Timeout:   time.Second * 120,
        Transport: netTransport,
    }

    req, _ := http.NewRequest("POST", reqUrl, body)

    req.Header.Set("Authorization", "Bearer "+token)
    req.Header.Set("Content-Type", writer.FormDataContentType())

    resp, err1 := client.Do(req)

    if err1 != nil {
        c.Err = model.NewAppError("send_video", err1.Error(), nil, "", http.StatusBadRequest)
        return
    } else {
        defer resp.Body.Close()
        var bodyBytes []byte
        bodyBytes, _ = ioutil.ReadAll(resp.Body)
        resp.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))

        if resp.StatusCode != http.StatusOK {
            fbErr := facebookgraph.FacebookErrorFromJson(resp.Body)
            fmt.Println("__ERROR___", fbErr)
            c.Err = model.NewAppErrorFromFacebookError("send_video", fbErr)
            return
        }

        // Do what ever we want with attachment_id result
    }

    ReturnStatusOK(w)
}

But always failed with error from Facebook:

{
  "error": {
    "message": "(#100) Upload attachment failure.",
    "type": "OAuthException",
    "code": 100,
    "error_subcode": 2018047,
    "fbtrace_id": "A2hkvhTQlmA98XmcrPvSy8O"
  }
}

The error subcode is: 2018047 according to Facebook docs:

Upload attachment failure. A common way to trigger this error is that the provided media type does not match type of file provided int the URL

I also try via cURL and everything's OK:

curl \
-F 'message={"attachment":{"type":"video", "payload":{"is_reusable":true}}}' \
-F 'filedata=@/home/cong/Downloads/123.mp4;type=video/mp4' \
"https://graph.facebook.com/v10.0/me/message_attachments?access_token=EAAUxUcj3C64BADxxsm70hZCXTMO0eQHmSp..."
{"attachment_id":"382840319882695"}% 

If I change "video" to "file", upload is success:

writer.WriteField("message", "{\"attachment\":{\"type\":\"file\", \"payload\":{\"is_reusable\":true}}}\")

But Facebook will send video as "file attachment" (can not view as video, must download to view). That's not what I want.

Can any one tell me how to fix this problem? Thank you very much!

1 Answers1

0

You shouldnt create write like this

fileWriter, err := writer.CreateFormFile("filedata", fileArray[0].Filename)

Because it will be created with header "Content-Type": "application/octet-stream" and facebook will send error file type. Replace this line like this:

    h := make(textproto.MIMEHeader)
    h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"; filename="%s"`,"filedata", fileArray[0].Filename)))
    h.Set("Content-Type", "video/mp4")

    fileWriter, err := writer.CreatePart(h)

Use CreatePart(header) and try again

Sơn xê kô
  • 179
  • 1
  • 4