Content-Length is zero when (HTTP) POSTing a local file by passing the io.Reader from os.Open to http.Post

huangapple go评论71阅读模式
英文:

Content-Length is zero when (HTTP) POSTing a local file by passing the io.Reader from os.Open to http.Post

问题

大家好:我有一个简短的问题,我想要发送一个文件(http)。我想到,由于os.Open返回一个io.Reader,而http.Post接受一个io.Reader,我不需要将文件读入内存中的一个单独步骤,而是可以直接传递Reader。
然而,Content-Length将被设置为零,这在某种程度上是有道理的,但不是我所需要的。

file, _ := os.Open("some file")
req, _ := http.NewRequest("POST", "some url", file)
dump, _ := httputil.DumpRequestOut(req, false)
fmt.Println(string(dump))

**我的问题:**我是否需要将文件读入内存(使用ioutil.ReadFile或类似方法)并创建一个新的Reader,还是有一种方法可以直接将文件的Reader传递给Post请求而不需要"读取"步骤?

我猜我可以通过使用file.Stat来设置Content-Length,但我想知道是否有更优雅的方法来做到这一点?

英文:

heya everyone: Got a short question, I would like to post(http) a file. I figured, since os.Open returns an io.Reader and http.Post takes an io.Reader I do not have read the file into memory in a separate step and can just pass around the Reader.
However, the Content-Length will be set to zero- which kinda makes sense but is not what I need.

file, _ := os.Open("some file")
req, _ := http.NewRequest("POST", "some url", file)
dump, _ := httputil.DumpRequestOut(req, false)
fmt.Println(string(dump))

My question: Do i have to read the file into memory (with ioutil.ReadFile or some such) and make a new reader or is there a way to pass the reader from the file directly to the Post request without the "Reading" step?

I guess I could set the Content-Length by getting it via file.Stat, but I was wondering if there is a more elegant way of doing this?

答案1

得分: 2

简短回答是:你关于将os.File传递给http.Request,并使用os.File.Stat()获取文件大小并将其设置为http.Request头部的做法是完全正确的。个人而言,我发现这是最简单的方法。

例如:

file, _ := os.Open("some file")
info, _ := file.Stat()
req, _ := http.NewRequest("POST", "http://bla.com", file)
req.ContentLength = info.Size()
dump, _ := httputil.DumpRequestOut(req, false)
fmt.Println(string(dump))

还要注意,根据http.Request.Write()文档(我引用):

> 如果Body存在,Content-Length小于等于0,并且TransferEncoding未设置为“identity”,Write会将“Transfer-Encoding: chunked”添加到头部。Body在发送后会被关闭。

因此,在这种情况下,客户端本身会为您执行(可以说是)最明智的操作。

英文:

Short answer is: you are absolutely right about giving the os.File to http.Request and about using os.File.Stat() to get the file size and setting it on the http.Request headers. Personally I've found it to be the simplest way around.

i.e.

file, _ := os.Open("some file")
info, _ := file.Stat()
req, _ := http.NewRequest("POST", "http://bla.com", file)
req.ContentLength = info.Size()
dump, _ := httputil.DumpRequestOut(req, false)
fmt.Println(string(dump))

Also note that as per http.Request.Write() documentation (I quote):

> If Body is present, Content-Length is <= 0 and TransferEncoding hasn't
> been set to "identity", Write adds "Transfer-Encoding: chunked" to the
> header. Body is closed after it is sent.

So in that case the client itself would do the (arguably) most sensible thing to do for you.

答案2

得分: 0

是的,可以直接将os.Open()返回的内容作为HTTP请求的主体进行传递。

但是,处理文件数据的内容长度只有两种方式。它们是不同的,但都围绕着接收方必须事先知道内容长度的概念,即从HTTP响应头中接收到的内容长度,或者必须使用一种允许不知道内容长度的机制,然后发送方必须有一种方式来表示它已经发送完数据。

  • 一种“经典”的方法,即使在HTTP/1.0中也适用,是在发送之前计算内容长度。这确实意味着要么将整个内容读入内存,要么进行stat调用(顺便说一下,你可以在打开的文件上进行stat调用)。

  • HTTP/1.1支持所谓的“分块传输编码(chunked transfer encoding)”,用于发送数据。在这种情况下,有效载荷被分成多个片段,每个片段依次发送,使用特定的分块格式。一个特殊的分块信号表示数据流的结束。在这种情况下,将Content-Type设置为0是可以的,但客户端必须支持分块编码(并非所有客户端都支持)。

英文:

Yes, it's possible to pass whatever os.Open() returned directly as the body of a HTTP request to be made.

But there are only two ways to deal with the content length of that file's data. They are different, but they both revolve around the idea that the receiving side either must know the content length in advance&mdash;that is, from the HTTP response headers is receives before the body or it must use a mechanism which allows to not know it, and then the sender must have a way to signal that it's done sending the data.

  • A "classic" one&mdash;working even with HTTP/1.0&mdash; is to calculate it before sending. This indeed means either reading the whole thing into memory or doing a stat call (you can do it on an opened file BTW).

  • HTTP/1.1 supports the so-called "chunked" transfer encoding of the data it sends.
    In this case, the payload is cut into pieces and each piece is sent in turn&mdash;using certain framing. A special frame signalizes the end of the data stream. In this case it's OK to set Content-Type to 0, but the client must support chunked encoding (not all do).

huangapple
  • 本文由 发表于 2016年12月29日 19:15:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/41378605.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定