Reputation: 13483
I am trying to issue a simple PUT
request to upload a file. http.NewRequest
accepts body
(as an io.Reader
). But passing os.File
as body
is NOT working, whereas reading it into a buffer first does the trick:
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: the issue was not setting the ContentLength
header (i.e. it was set to the default of 0); this resulted in server not processing the upload. When using a buffer, golang does set the header to buffer length, resulting in different behavior.
Is the ContentLength
header semantics server dependent?? Browsing SO I got the impression that the header is optional, which is clearly not the case here.
Upvotes: 1
Views: 1858
Reputation: 711
This source code may help.
// /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 }
}
}
Upvotes: 1