如何使用os.Open()' s返回值作为http.Post()的第三个参数并设置Content-Length?

时间:2016-08-02 02:58:20

标签: file-upload go

http.Post()的第三个参数允许io.Reader,这意味着os.Open()的返回值应该有效。但是下面的代码会得到意想不到的结果,换句话说,它不会正确设置Content-Length。也许File类型没有实现某些东西。有没有正确的方法可以Content-Length设置*File

package main

import (
    "bytes"
    "io/ioutil"
    "log"
    "net/http"
    "net/http/httptest"
    "os"
)

var sample = []byte(`hello`)

func main() {
    ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        log.Println(r.Header)
        if int(r.ContentLength) != len(sample) {
            log.Fatal("Unexpected Content-Length:", r.ContentLength)
        }
        w.Header().Set("Content-Type", "application/json")
        w.Write([]byte(`{}`))
    }))
    defer ts.Close()

    file, err := ioutil.TempFile(os.TempDir(), "")
    if err != nil {
        log.Fatal(err)
    }
    defer os.Remove(file.Name())
    file.Write(sample)

    // This works
    buf, err := ioutil.ReadFile(file.Name())
    if err != nil {
        log.Fatal(err)
    }
    _, err = http.Post(ts.URL, "application/octet-stream", bytes.NewBuffer(buf))
    if err != nil {
        log.Fatal(err)
    }

    // This looks fine in my opinion, though it doesn't set Content-Length
    f, err := os.Open(file.Name())
    if err != nil {
        log.Fatal(err)
    }
    _, err = http.Post(ts.URL, "application/octet-stream", f)
    if err != nil {
        log.Fatal(err)
    }
}

输出:

2009/11/10 23:00:00 map[Content-Type:[application/octet-stream] Accept-Encoding:[gzip] User-Agent:[Go-http-client/1.1] Content-Length:[5]]
2009/11/10 23:00:00 map[Content-Type:[application/octet-stream] Accept-Encoding:[gzip] User-Agent:[Go-http-client/1.1]]
2009/11/10 23:00:00 Unexpected Content-Length:-1

https://play.golang.org/p/hJLN2H9Y9p

2 个答案:

答案 0 :(得分:0)

如果您查看NewRequest的来源,您会发现contentLength是针对特定输入类型专门处理的,而文件阅读器并不是其中之一。您必须手动设置Content-Length标头,如果重要的那样[chunked也应该正常工作,除非您发送到旧服务器impl]。

答案 1 :(得分:0)

如果要添加Content-Length,则需要对文件进行统计以获取大小。 ContentLength不会自动计算,因为os.File可能没有有用的大小。

f, err := os.Open(file.Name())
if err != nil {
    log.Fatal(err)
}

req, err := http.NewRequest("POST", ts.URL, f)
if err != nil {
    log.Fatal(err)
}

stat, err := f.Stat()
if err != nil {
    log.Fatal(err)
}

req.ContentLength = stat.Size()
req.Header.Set("Content-Type", "application/octet-stream")

resp, err = http.Do(req)
...