跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

Golang 使用多部分将大文件上传到外部 API。如何避免`io.Copy(io.Writer, io.Reader)`问题

问题内容 我的目标是使用 golang 的内置 net/http 包将一个大文件上传到
POST https://somehost/media
。 Api调用的HTTP格式
POST /media HTTP/1.1 Host: somehost Content-Length: 434 Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW ------WebKitFormBoundary7MA4YWxkTrZu0gW Content-Disposition: form-data; name="detail" More and more detail ------WebKitFormBoundary7MA4YWxkTrZu0gW Content-Disposition: form-data; name="file"; filename="some_big_video.mp4" Content-Type: (data) ------WebKitFormBoundary7MA4YWxkTrZu0gW--
在 golang 中,这是代码。
package main import ( "fmt" "bytes" "mime/multipart" "os" "path/filepath" "io" "net/http" "io/ioutil" ) func main() { url := "https://somehost/media" method := "POST" payload := &bytes.Buffer{} writer := multipart.NewWriter(payload) _ = writer.WriteField("details", "more and more details") file, errFile3 := os.Open("/Users/vajahat/Downloads/some_big_video.mp4") defer file.Close() part3,errFile3 := writer.CreateFormFile("file","some_big_video.mp4") _, errFile3 = io.Copy(part3, file) if errFile3 != nil { fmt.Println(errFile3) return } err := writer.Close() if err != nil { fmt.Println(err) return } client := &http.Client {} req, err := http.NewRequest(method, url, payload) if err != nil { fmt.Println(err) return } req.Header.Set("Content-Type", writer.FormDataContentType()) res, err := client.Do(req) if err != nil { fmt.Println(err) return } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { fmt.Println(err) return } fmt.Println(string(body)) }
如何避免
io.Copy(io.Writer, io.Reader)
问题 立即学习 “ go语言免费学习笔记(深入) ”; 上面的代码工作正常,但是在
_, errFile3 = io.Copy(part3, file)
行上。这实际上将文件中的所有内容复制到主内存中。 微信接口库:PHPToQyWeixin PHP调用企业微信API接口底层类库 下载 如何避免这种情况? 有什么办法,我可以通过
multipart-formdata
将大文件流式传输到 api? 该程序将在远程服务器上运行。如果打开一个非常大的文件,可能会崩溃。 正确答案 使用 io.Pipe 和 goroutine 将文件复制到请求而不加载整个文件内存中的文件。
pr, pw := io.Pipe() writer := multipart.NewWriter(pw) ct := writer.FormDataContentType() go func() { _ = writer.WriteField("details", "more and more details") file, err := os.Open("/Users/vajahat/Downloads/some_big_video.mp4") if err != nil { pw.CloseWithError(err) return } defer file.Close() part3, err := writer.CreateFormFile("file", "some_big_video.mp4") if err != nil { pw.CloseWithError(err) return } _, err = io.Copy(part3, file) if err != nil { pw.CloseWithError(err) return } pw.CloseWithError(writer.Close()) }() client := &http.Client{} req, err := http.NewRequest(method, url, pr) if err != nil { fmt.Println(err) return } req.Header.Set("Content-Type", ct) // remaining code as before

相关文章