【问题标题】:Forward a file upload to other server将文件上传转发到其他服务器
【发布时间】:2021-11-12 21:49:18
【问题描述】:

我正在尝试通过 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 工作?

非常感谢!

【问题讨论】:

  • 根据我的经验,golang 不会添加任何额外的标题,而 curl 可能会。也许尝试使用-v 运行您的 curl 命令,看看是否有任何可能相关的额外标头。
  • 您在上传响应中是否收到任何子错误代码?错误响应中的子代码可能更相关。
  • @sigkilled:子代码错误为 2018047:“上传附件失败。触发此错误的常见方法是提供的媒体类型与 URL 中提供的文件类型不匹配”。这似乎是我的问题,但我不确定如何设置媒体类型以匹配

标签: http go curl facebook-graph-api file-upload


【解决方案1】:

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

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

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

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

【讨论】:

    猜你喜欢
    • 2023-03-25
    • 1970-01-01
    • 2017-11-18
    • 1970-01-01
    • 1970-01-01
    • 2020-05-26
    • 1970-01-01
    • 2017-01-22
    • 1970-01-01
    相关资源
    最近更新 更多