最近在用go语言做巨量视频上传接口时遇到问题,记录一下解决过程
巨量视频上传接口显示是要用multipart/form-data类型数据来进行POST
然后我先用go自带的multipart库请求了一下,却返回了错误:No permission to operate account 0,意思是没有权限操作账户0,代码如下:
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
_ = bodyWriter.WriteField("advertiser_id", "1724433764965395")
_ = bodyWriter.WriteField("video_signature", md5str)
file, _ := os.Open("./v.mp4")
defer file.Close()
part, _ := bodyWriter.CreateFormFile("video_file", "v.mp4")
_, _ = io.Copy(part, file)
url := "https://ad.oceanengine.com/open_api/2/file/video/ad/"
clientOcean := &http.Client{}
req, _ := http.NewRequest("POST", url, bodyBuf)
req.Header.Set("Access-Token", "cf3eac6784537bd4def62a1f6548cf3f679349a3")
req.Header.Set("X-Debug-Mode", "1")
req.Header.Set("Content-Type", bodyWriter.FormDataContentType())
defer bodyWriter.Close()
resp, err := clientOcean.Do(req)
check(err)
defer resp.Body.Close()
res, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(res))
panic("ok")
返回错误如图:
错误可以看出,request_params为空,很明显巨量那边没有获取到任何数据,后来在网上查了很多资料,都没找到解决办法,这个问题已经困扰了我两天。
后来我找来了几个三方请求库,问题依旧,直到我用了httpclient库(github.com/ddliu/go-httpclient),发现竟然能正常请求。
resp2, err := httpclient.
WithHeader("Access-Token", "cf3eac6784537bd4def62a1f6548cf3f679349a3").
WithHeader("X-Debug-Mode", "1").
Post(url, map[string]string {
"advertiser_id": "1724433764965395",
"video_signature": md5str,
"@video_file": "./v.mp4",
})
check(err)
body1, _ := resp2.ToString()
fmt.Println(body1)
然后我用httputil来输出请求元数据,意外发现两者居然有细微的差别,没用三方类库的请求结尾竟然少了一个结束boundary界定符。
后来经过排查,发现竟然和defer bodyWriter.Close()这行有关,猜测有可能是bodyWriter过早关闭导致,用变量获取FormDataContentType,再改成err1 := bodyWriter.Close(),移到前面就可以了,最终完整代码如下:
package main
import (
"os"
"io"
"fmt"
"bytes"
"net/http"
"io/ioutil"
"crypto/md5"
"mime/multipart"
"net/http/httputil"
"github.com/ddliu/go-httpclient"
)
func main() {
f, _ := os.Open("./v.mp4")
defer f.Close()
md5Handle := md5.New()
_, _ = io.Copy(md5Handle, f)
md := md5Handle.Sum(nil)
md5str := fmt.Sprintf("%x", md)
bodyBuf := &bytes.Buffer{}
bodyWriter := multipart.NewWriter(bodyBuf)
_ = bodyWriter.WriteField("advertiser_id", "1724433764965395")
_ = bodyWriter.WriteField("video_signature", md5str)
file, _ := os.Open("./v.mp4")
defer file.Close()
part, _ := bodyWriter.CreateFormFile("video_file", "v.mp4")
_, _ = io.Copy(part, file)
url := "https://ad.oceanengine.com/open_api/2/file/video/ad/"
contentType := bodyWriter.FormDataContentType()
err1 := bodyWriter.Close()
check(err1)
clientOcean := &http.Client{}
req, _ := http.NewRequest("POST", url, bodyBuf)
req.Header.Set("Access-Token", "cf3eac6784537bd4def62a1f6548cf3f679349a3")
req.Header.Set("X-Debug-Mode", "1")
req.Header.Set("Content-Type", contentType)
dump, err := httputil.DumpRequestOut(req, true)
if err == nil {
fmt.Printf("%s\n", dump)
}
resp, err := clientOcean.Do(req)
check(err)
defer resp.Body.Close()
res, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(res))
panic("ok")
// post file(multipart) use httpclient
resp2, err := httpclient.
WithHeader("Access-Token", "cf3eac6784537bd4def62a1f6548cf3f679349a3").
WithHeader("X-Debug-Mode", "1").
WithOption(httpclient.OPT_DEBUG, true).
Post(url, map[string]string {
"advertiser_id": "1724433764965395",
"video_signature": md5str,
"@video_file": "./v.mp4",
})
check(err)
body1, _ := resp2.ToString()
fmt.Println(body1)
}
func check(err error) {
if err != nil{
fmt.Println(err)
panic(err)
}
}
至此,问题得到完美解决,这对于不喜欢用三方类库的是个好方法,毕竟用三方类库编译出来的文件要稍微大一点,分享一下希望对大家有所帮助吧。