首页 > 解决方案 > 将文件上传转发到其他服务器

问题描述

我正在尝试通过 Facebook Graph API 上传本地视频。

这是官方文档:https ://developers.facebook.com/docs/messenger-platform/reference/attachment-upload-api/

  -F 'message={"attachment":{"type":"image", "payload":{"is_reusable":true}}}' \
  -F 'filedata=@/tmp/shirt.png;type=image/png' \
  "https://graph.facebook.com/v12.0/me/message_attachments?access_token=<PAGE_ACCESS_TOKEN>"

这是我的 Golang 代码:

func uploadVideoStream(c *Context, w http.ResponseWriter, r *http.Request) {
    if err := r.ParseMultipartForm(MAXIMUM_PLUGIN_FILE_SIZE); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    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)

    _message := uploadVideoData{
        Message: uploadVideoDataMessage{
            Attachment: uploadVideoDataMessageAttachment{
                Type: "video",
                Payload: uploadVideoDataMessageAttachmentPayload{
                    IsReusable: true,
                },
            },
        },
    }

    // add form fields
    writer.WriteField("message", _message.Message.ToJson())

    // 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 := "EAAUxUcj3C64BADxxsm70hZCXTMO0eQHmSpV..."
    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 {
        fmt.Println("error1", err1)
        c.Err = model.NewAppError("EditComment", 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 {
            fmt.Println("error2", resp.Body)
            fbErr := facebookgraph.FacebookErrorFromJson(resp.Body)
            c.Err = model.NewAppErrorFromFacebookError("EditComment", fbErr)
            return
        }

        fmt.Println("UPLOAD VIDEO SUCCESS", resp.Body)

        ReturnStatusOK(w)
    }
}

这是上面代码的一些结构:

type uploadVideoDataMessageAttachmentPayload struct {
    IsReusable bool `json:"is_reusable"`
}

type uploadVideoDataMessageAttachment struct {
    Type    string                                  `json:"type"`
    Payload uploadVideoDataMessageAttachmentPayload `json:"payload"`
}

type uploadVideoDataMessage struct {
    Attachment uploadVideoDataMessageAttachment `json:"attachment"`
}

type uploadVideoData struct {
    Message uploadVideoDataMessage `json:"message"`
}

func (o uploadVideoData) ToJson() string {
    b, _ := json.Marshal(o)
    return string(b)
}

func (o uploadVideoDataMessage) ToJson() string {
    b, _ := json.Marshal(o)
    return string(b)
}

对于上述请求,Facebook 总是返回失败:

(#100) Upload attachment failure.

我正在尝试 CURL,并且成功:

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"}% 

谁能告诉我我错过了哪一部分,以及如何使我的请求等效于 CURL 工作?

非常感谢!

标签: httpgocurlfacebook-graph-apifile-upload

解决方案


感谢@sigkilled 按照他的建议,我检查了 subcode_error 并发现此错误是由我设置的文件类型之间的匹配文件类型未命中引起的:

_message := uploadVideoData{
        Message: uploadVideoDataMessage{
            Attachment: uploadVideoDataMessageAttachment{
                Type: "video",
                Payload: uploadVideoDataMessageAttachmentPayload{
                    IsReusable: true,
                },
            },
        },
    }

以及 Facebook 服务器检测到的文件类型。然后我将“视频”更改为“文件”并成功!

非常感谢,你教给我的不仅仅是这个问题!


推荐阅读