将文件上传到Google存储空间而不保存到内存

时间:2019-11-26 14:34:53

标签: go file-upload upload google-cloud-storage object-storage

我想直接从前端通过后端将文件上传到Google存储桶,而不必先将其完全保存在服务器的内存中。我已经从Google文档中添加了一个类似于example的端点,它可以正常工作。但是,我不确定是否会先将整个文件保存到内存中,因为这可能会导致在上传较大文件时出现问题。

如果它先将文件保存到内存中,我该如何更改代码,以便将上传的内容直接流式传输到Google Storage。类似问题的答案并不能阐明我的问题。

谢谢

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()
}

2 个答案:

答案 0 :(得分:3)

  

FormFile返回提供的表单密钥的第一个文件。如有必要,FormFile调用ParseMultipartForm和ParseForm。

https://golang.org/pkg/net/http/#Request.FormFile

  

ParseMultipartForm将请求正文解析为multipart / form-data。整个请求正文将被解析,其文件部分的总计maxMemory字节最多存储在内存中,其余部分存储在磁盘上的临时文件中。

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,以及一些不适合内存的磁盘空间。

因此,只有在读取了整个文件之后,上传才会开始,但并非所有文件都保存在内存中。如果这不是您想要的,请使用Request.MultipartReader代替ParseMultipartForm:

  

如果是分段/表单数据或分段/混合POST请求,则MultipartReader返回MIME分段阅读器,否则返回nil和错误。使用此函数代替ParseMultipartForm将请求主体作为流进行处理。

答案 1 :(得分:2)

正如彼得在对问题和答案的评论中指出的那样,请使用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注释替换为以适当的错误状态响应客户端的代码。记录一些错误也可能很有用。