问题
金山云对象存储(KS3)的"PUT Object" API老是抛"MissingContentLength"!
原因
- go http.NewRequest()接收os.File等非bytes.Buffer, strings.Buffer类型时无法设置ContentLength
注意: req.Header.set("Content-Length", xxx)是无效的! 最终以http.Request.ContentLength为准!
解决
- 在http.NewRequest()时传递http.NoBody构建req对象后再自行设置ContentLength与GetBody
req, err := http.NewRequest(http.MethodPut, getUrl("test2.go"), file)
if err != nil {
panic(err)
}
sta, _ := file.Stat()
req.ContentLength = sta.Size()
完整源码
func main() {
upload()
}
func upload() {
file, err := os.Open(`C:UsersJasonDesktopstructmap.go`)
if err != nil {
panic(err)
}
defer file.Close()
file.Seek(0, io.SeekStart)
req, err := http.NewRequest(http.MethodPut, getUrl("test2.go"), file)
if err != nil {
panic(err)
}
sta, _ := file.Stat()
req.ContentLength = sta.Size()
//req.Header.Set("Content-Length", strconv.FormatInt(sta.Size(), 10)) // 见上,设置Content-Length毛用...
date := utcNow()
req.Header.Set("Date", date)
req.Header.Set("x-kss-acl", "private")
req.Header.Set("Authorization", "KSS "+AK+":"+signature(http.MethodPut, "", "", date, "/hezhaowu/test2.go", "x-kss-acl:private"))
bs, _ := httputil.DumpRequest(req, false)
fmt.Printf("%s
", bs)
rsp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer rsp.Body.Close()
fmt.Println(rsp.Proto, rsp.Status)
io.Copy(os.Stdout, rsp.Body)
}
func download() {
req, err := http.NewRequest(http.MethodGet, getUrl("struct.go"), nil)
if err != nil {
panic(err)
}
date := utcNow()
req.Header.Set("Date", date)
req.Header.Set("Authorization", "KSS "+AK+":"+signature(http.MethodGet, "", "", date, "/hezhaowu/struct.go"))
rsp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer rsp.Body.Close()
fmt.Println(rsp.Proto, rsp.Status)
io.Copy(os.Stdout, rsp.Body)
}
func signature(method string, contentMd5 string, contentType string, date string, name string, xobs ...string) string {
var sb bytes.Buffer
sb.WriteString(method)
sb.WriteByte('
')
sb.WriteString(contentMd5)
sb.WriteByte('
')
sb.WriteString(contentType)
sb.WriteByte('
')
sb.WriteString(date)
sb.WriteByte('
')
for _, v := range xobs {
sb.WriteString(v)
sb.WriteByte('
')
}
sb.WriteString(name)
h := hmac.New(sha1.New, []byte(SK))
h.Write(sb.Bytes())
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}
func getUrl(name string) string {
return "https://" + DM + "/" + name
}
func utcNow() string {
const layout = "Mon, 2 Jan 2006 15:04:05 GMT"
return time.Now().UTC().Format(layout)
}