golang中文件的HTTP PUT请求体:如果未正确设置ContentLength,则上载失败



我正试图发出一个简单的PUT请求来上传文件。http.NewRequest接受body(作为io.Reader(。但是将os.File作为body传递是不起作用的,而首先将其读取到缓冲区中则可以:

file, _ := os.Open(filePath)
// request, _ := http.NewRequest("PUT", myURL, file)
// ^^^ why does this not work???
var buf bytes.Buffer
tee := io.TeeReader(file, &buf)
ioutil.ReadAll(tee)                                 
request, _ := http.NewRequest("PUT", myURL, &buf)   // this works fine
request.Header.Set("Content-Type", "application/octet-stream")
http.DefaultClient.Do(request)

EDIT:问题不是设置ContentLength标头(即,它被设置为默认值0(;这导致服务器无法处理上传。使用缓冲区时,golang将标头设置为缓冲区长度,从而导致不同的行为。

ContentLength标头语义是否依赖于服务器??浏览所以我得到的印象是标题是可选的,这显然不是这里的情况。

此源代码可能会有所帮助。

// /usr/lib/go/src/net/http/request.go:872
if body != nil {
switch v := body.(type) {
case *bytes.Buffer:
req.ContentLength = int64(v.Len())
buf := v.Bytes()
req.GetBody = func() (io.ReadCloser, error) {
r := bytes.NewReader(buf)
return ioutil.NopCloser(r), nil
}
case *bytes.Reader:
req.ContentLength = int64(v.Len())
snapshot := *v
req.GetBody = func() (io.ReadCloser, error) {
r := snapshot
return ioutil.NopCloser(&r), nil
}
case *strings.Reader:
req.ContentLength = int64(v.Len())
snapshot := *v
req.GetBody = func() (io.ReadCloser, error) {
r := snapshot
return ioutil.NopCloser(&r), nil
}
default:
// This is where we'd set it to -1 (at least
// if body != NoBody) to mean unknown, but
// that broke people during the Go 1.8 testing
// period. People depend on it being 0 I
// guess. Maybe retry later. See Issue 18117.
}
// For client requests, Request.ContentLength of 0
// means either actually 0, or unknown. The only way
// to explicitly say that the ContentLength is zero is
// to set the Body to nil. But turns out too much code
// depends on NewRequest returning a non-nil Body,
// so we use a well-known ReadCloser variable instead
// and have the http package also treat that sentinel
// variable to mean explicitly zero.
if req.GetBody != nil && req.ContentLength == 0 {
req.Body = NoBody
req.GetBody = func() (io.ReadCloser, error) { return NoBody, nil }
}
}