计算多部分表单提交的内容长度

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

Computing the Content Length of a Multipart form Post

问题

我正在尝试上传一个文件而不将其加载到内存中,如下所示。像S3这样的服务在这种情况下需要设置Content-Length。是否有Go语言内置的方法可以实现这一点,还是我需要自己计算它。

package main

import (
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
)

func newfileUploadRequest(uri string, params map[string]string, paramName, path string) (*http.Request, chan error, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, nil, err
	}
	bodyReader, bodyWriter := io.Pipe()
	multiWriter := multipart.NewWriter(bodyWriter)
	errChan := make(chan error, 1)
	go func() {
		defer bodyWriter.Close()
		defer file.Close()
		part, err := multiWriter.CreateFormFile(paramName, filepath.Base(path))
		if err != nil {
			errChan <- err
			return
		}
		if _, err := io.Copy(part, file); err != nil {
			errChan <- err
			return
		}
		for k, v := range params {
			if err := multiWriter.WriteField(k, v); err != nil {
				errChan <- err
				return
			}
		}
		errChan <- multiWriter.Close()
	}()
	req, err := http.NewRequest("POST", uri, bodyReader)
	return req, errChan, err
}

非常感谢您的帮助。

英文:

I'm trying to upload a file without loading it into memory, as shown below. Services like S3 need a Content-Length set in such cases. Is there a go-lang built-in to do that, or do I have to compute it myself.

package main

import (
	&quot;io&quot;
	&quot;mime/multipart&quot;
	&quot;net/http&quot;
	&quot;os&quot;
	&quot;path/filepath&quot;
)

func newfileUploadRequest(uri string, params map[string]string, paramName, path string) (*http.Request, chan error, error) {
	file, err := os.Open(path)
	if err != nil {
		return nil, nil, err
	}
	bodyReader, bodyWriter := io.Pipe()
	multiWriter := multipart.NewWriter(bodyWriter)
	errChan := make(chan error, 1)
	go func() {
		defer bodyWriter.Close()
		defer file.Close()
		part, err := multiWriter.CreateFormFile(paramName, filepath.Base(path))
		if err != nil {
			errChan &lt;- err
			return
		}
		if _, err := io.Copy(part, file); err != nil {
			errChan &lt;- err
			return
		}
		for k, v := range params {
			if err := multiWriter.WriteField(k, v); err != nil {
				errChan &lt;- err
				return
			}
		}
		errChan &lt;- multiWriter.Close()
	}()
	req, err := http.NewRequest(&quot;POST&quot;, uri, bodyReader)
	return req, errChan, err
}

Any help would be much appreciated.

答案1

得分: 5

http.Request.Write的文档中指出:

> 如果存在Body,Content-Length小于等于0且TransferEncoding未设置为"identity",Write会将"Transfer-Encoding: chunked"添加到头部

这意味着如果你没有设置Content-Lengthhttp.Request.Write将使用分块传输编码。这个编码被添加到HTTP/1.1中,以消除像这样的流式传输需要计算Content-Length的需求。

因此,通常Go程序使用分块编码,不需要设置Content-Length。任何现代的HTTP堆栈都应该支持分块传输编码。

然而,S3不支持分块传输编码,所以我认为你需要自己计算Content-Length

英文:

In the docs for http.Request.Write it states

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

Which means if you don't set a Content-Length, http.Request.Write will use chunked transfer encoding. This was added to HTTP/1.1 to get rid of the need to calculate the Content-Length for streaming transfers just like this.

So normally Go programs use chunked encoding and there is no need to set Content-Length. Any modern HTTP stack should support chunked transfer encoding.

However S3 does not support chunked transfer encoding, so I think you'll have to calculate Content-Length yourself.

huangapple
  • 本文由 发表于 2014年5月5日 06:20:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/23462230.html
匿名

发表评论

匿名网友

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

确定