我的目標是使用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中文網其他相關文章!