正在将文件上传到 Google 存储而不将其保存到内存
Uploading file to Google Storage without saving it to memory
我想从前端直接通过后端将文件上传到 Google 存储桶中,而不是先将其完全保存在服务器的内存中。我添加了一个类似于 Google 文档中的 example 的端点并且它有效。但是,我不确定这是否会先将整个文件保存到内存中,因为这可能会导致上传较大文件时出现问题。
如果它首先将文件保存到内存中,我如何更改代码以便它直接将上传流式传输到 Google 存储。类似问题的答案没有阐明我的问题。
谢谢
func Upload(c *gin.Context) {
file, _, _ := c.Request.FormFile("image")
ctx := context.Background()
client, err := storage.NewClient(ctx)
if err != nil {
fmt.Printf("Failed to create client with error: %v", err)
return
}
bucket := client.Bucket("test-bucket")
w := bucket.Object("testfile").NewWriter(ctx)
w.ContentType = "image/jpeg"
io.Copy(w, file)
w.Close()
}
FormFile returns the first file for the provided form key. FormFile calls ParseMultipartForm and ParseForm if necessary.
https://golang.org/pkg/net/http/#Request.FormFile
ParseMultipartForm parses a request body as multipart/form-data. The whole request body is parsed and up to a total of maxMemory bytes of its file parts are stored in memory, with the remainder stored on disk in temporary files.
https://golang.org/pkg/net/http/#Request.ParseMultipartForm
在撰写本文时,FormFile passes 32 MB as the maxMemory argument。
因此使用此代码,每个请求最多需要 32 MB 的内存,外加 googleapi.DefaultUploadChunkSize, which is currently 8 MB,以及一些磁盘 space 用于内存中无法容纳的所有内容.
所以在读取整个文件之前不会开始上传,但并不是所有文件都保存在内存中。如果这不是您想要的,请使用 Request.MultipartReader 而不是 ParseMultipartForm:
MultipartReader returns a MIME multipart reader if this is a multipart/form-data or a multipart/mixed POST request, else returns nil and an error. Use this function instead of ParseMultipartForm to process the request body as a stream.
正如彼得对问题和答案的评论中所述,使用 multipart reader directly 阅读请求正文。
func Upload(c *gin.Context) {
mr, err := c.Request.MultipartReader()
if err != nil {
// handle error
return
}
var foundImage bool
for {
p, err := mr.NextPart()
if err == io.EOF {
break
}
if err != nil {
// handle error
return
}
if p.FormName() == "image" {
foundImage = true
ctx := context.Background()
client, err := storage.NewClient(ctx)
if err != nil {
// handle error
return
}
bucket := client.Bucket("test-bucket")
w := bucket.Object("testfile").NewWriter(ctx)
w.ContentType = "image/jpeg"
if _, err := io.Copy(w, p); err != nil {
// handle error
return
}
if err := w.Close(); err != nil {
// handle error
return
}
}
}
if !imageFound {
// handle error
}
}
将 // handle error
注释替换为以适当的错误状态响应客户端的代码。记录一些错误可能也很有用。
我想从前端直接通过后端将文件上传到 Google 存储桶中,而不是先将其完全保存在服务器的内存中。我添加了一个类似于 Google 文档中的 example 的端点并且它有效。但是,我不确定这是否会先将整个文件保存到内存中,因为这可能会导致上传较大文件时出现问题。
如果它首先将文件保存到内存中,我如何更改代码以便它直接将上传流式传输到 Google 存储。类似问题的答案没有阐明我的问题。
谢谢
func Upload(c *gin.Context) {
file, _, _ := c.Request.FormFile("image")
ctx := context.Background()
client, err := storage.NewClient(ctx)
if err != nil {
fmt.Printf("Failed to create client with error: %v", err)
return
}
bucket := client.Bucket("test-bucket")
w := bucket.Object("testfile").NewWriter(ctx)
w.ContentType = "image/jpeg"
io.Copy(w, file)
w.Close()
}
FormFile returns the first file for the provided form key. FormFile calls ParseMultipartForm and ParseForm if necessary.
https://golang.org/pkg/net/http/#Request.FormFile
ParseMultipartForm parses a request body as multipart/form-data. The whole request body is parsed and up to a total of maxMemory bytes of its file parts are stored in memory, with the remainder stored on disk in temporary files.
https://golang.org/pkg/net/http/#Request.ParseMultipartForm
在撰写本文时,FormFile passes 32 MB as the maxMemory argument。
因此使用此代码,每个请求最多需要 32 MB 的内存,外加 googleapi.DefaultUploadChunkSize, which is currently 8 MB,以及一些磁盘 space 用于内存中无法容纳的所有内容.
所以在读取整个文件之前不会开始上传,但并不是所有文件都保存在内存中。如果这不是您想要的,请使用 Request.MultipartReader 而不是 ParseMultipartForm:
MultipartReader returns a MIME multipart reader if this is a multipart/form-data or a multipart/mixed POST request, else returns nil and an error. Use this function instead of ParseMultipartForm to process the request body as a stream.
正如彼得对问题和答案的评论中所述,使用 multipart reader directly 阅读请求正文。
func Upload(c *gin.Context) {
mr, err := c.Request.MultipartReader()
if err != nil {
// handle error
return
}
var foundImage bool
for {
p, err := mr.NextPart()
if err == io.EOF {
break
}
if err != nil {
// handle error
return
}
if p.FormName() == "image" {
foundImage = true
ctx := context.Background()
client, err := storage.NewClient(ctx)
if err != nil {
// handle error
return
}
bucket := client.Bucket("test-bucket")
w := bucket.Object("testfile").NewWriter(ctx)
w.ContentType = "image/jpeg"
if _, err := io.Copy(w, p); err != nil {
// handle error
return
}
if err := w.Close(); err != nil {
// handle error
return
}
}
}
if !imageFound {
// handle error
}
}
将 // handle error
注释替换为以适当的错误状态响应客户端的代码。记录一些错误可能也很有用。