英文:
Send os.Stdin via http POST without loading file into memory
问题
我正在尝试通过POST请求将文件发送到服务器。为了实现这一目标,我使用以下代码:
func newfileUploadRequest(uri string) (*http.Request, error) {
body := new(bytes.Buffer)
writer := multipart.NewWriter(body)
part, err := writer.CreateFormFile("file", "file")
if err != nil {
return nil, err
}
io.Copy(part, os.Stdin)
err = writer.Close()
if err != nil {
return nil, err
}
request, err := http.NewRequest("POST", uri, body)
if err != nil {
return nil, err
}
request.Header.Set("Content-Type", writer.FormDataContentType())
return request, nil
}
func main() {
r, err := newfileUploadRequest("http://localhost:8080/")
if err != nil {
panic(err)
}
client := &http.Client{}
resp, err := client.Do(r)
if err != nil {
panic(err)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
print(string(body))
}
虽然这段代码运行良好,但我了解到io.Copy
会在发送POST请求之前将整个文件复制到内存中。大文件(多个GB)会导致问题。有没有办法避免这种情况?我找到了这个链接,但它只是说要使用io.Copy
。
英文:
I am trying to send a file to a server via a POST request. To accomplish this, I use the following code:
func newfileUploadRequest(uri string) (*http.Request, error) {
body := new(bytes.Buffer)
writer := multipart.NewWriter(body)
part, err := writer.CreateFormFile("file", "file")
if err != nil {
return nil, err
}
io.Copy(part, os.Stdin)
err = writer.Close()
if err != nil {
return nil, err
}
request, err := http.NewRequest("POST", uri, body)
if err != nil {
return nil, err
}
request.Header.Set("Content-Type", writer.FormDataContentType())
return request, nil
}
func main() {
r, err := newfileUploadRequest("http://localhost:8080/")
if err != nil {
panic(err)
}
client := &http.Client{}
resp, err := client.Do(r)
if err != nil {
panic(err)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
print(string(body))
}
While this works well, it is my understanding that io.Copy will copy the entire file into memory before the POST request is sent. Large files (multiple GB) will create issues. Is there a way to prevent this? I found this, but that simply says to use io.Copy.
答案1
得分: 2
你可以通过使用 io.Pipe 和一个将文件从管道复制到内存的 goroutine 来避免复制内存中的数据:
func newfileUploadRequest(uri string) (*http.Request, error) {
r, w := io.Pipe()
writer := multipart.NewWriter(w)
go func() {
part, err := writer.CreateFormFile("file", "file")
if err != nil {
w.CloseWithError(err)
return
}
_, err = io.Copy(part, os.Stdin)
if err != nil {
w.CloseWithError(err)
return
}
err = writer.Close()
if err != nil {
w.CloseWithError(err)
return
}
}()
request, err := http.NewRequest("POST", uri, r)
if err != nil {
return nil, err
}
request.Header.Set("Content-Type", writer.FormDataContentType())
return request, nil
}
这段代码使用了 io.Pipe
和 goroutine 来实现了文件的上传。在 newfileUploadRequest
函数中,我们创建了一个管道 r, w := io.Pipe()
,然后使用 multipart.NewWriter(w)
创建了一个 multipart.Writer
对象。接下来,我们启动了一个 goroutine,该 goroutine 在管道中创建了一个表单文件,并将文件内容从标准输入流 os.Stdin
复制到表单文件中。最后,我们关闭了 multipart.Writer
并创建了一个 http.Request
对象,将管道作为请求的主体,并设置了正确的请求头。
英文:
You can avoid copying the data in memory by using an io.Pipe and a goroutine that copies from the file to the pipe:
func newfileUploadRequest(uri string) (*http.Request, error) {
r, w := io.Pipe()
writer := multipart.NewWriter(w)
go func() {
part, err := writer.CreateFormFile("file", "file")
if err != nil {
w.CloseWithError(err)
return
}
_, err = io.Copy(part, os.Stdin)
if err != nil {
w.CloseWithError(err)
return
}
err = writer.Close()
if err != nil {
w.CloseWithError(err)
return
}
}()
request, err := http.NewRequest("POST", uri, r)
if err != nil {
return nil, err
}
request.Header.Set("Content-Type", writer.FormDataContentType())
return request, nil
}
答案2
得分: 1
使用io.Pipe()
- https://golang.org/pkg/io/#Pipe
你需要使用一个 goroutine,但这并不难做到。如果你需要一个具体的示例,请留下评论,我会为你提供一个。
英文:
Use io.Pipe()
- https://golang.org/pkg/io/#Pipe
You will need to use a goroutine but it isn't too hard to do. If you need a specific example leave a comment and I'll get one for you.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论