我的目标是使用 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: <Content-Type header here> (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)
问题
上面的代码工作正常,但是在 _, errFile3 = io.Copy(part3, file)
行上。这实际上将文件中的所有内容复制到主内存中。
如何避免这种情况?
有什么办法,我可以通过 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
以上是Golang 使用多部分将大文件上传到外部 API。如何避免`io.Copy(io.Writer, io.Reader)`问题的详细内容。更多信息请关注PHP中文网其他相关文章!