简体   繁体   English

将文件上传到 Google Storage 而不将其保存到内存

[英]Uploading file to Google Storage without saving it to memory

I want to upload files from the frontend directly through the backend into a Google Storage bucket, without saving it entirely in memory on the server first.我想从前端直接通过后端将文件上传到 Google Storage 存储桶中,而不是先将其完全保存在服务器的内存中。 I've added an endpoint similar to theexample from the Google docs and it works.我添加了一个类似于 Google 文档中的示例的端点,它可以工作。 However, I'm not sure if this will save the entire file to memory first, since this could lead to issues when uploading larger files.但是,我不确定这是否会先将整个文件保存到内存中,因为这可能会导致上传较大文件时出现问题。

If it saves the file to memory first, how could I change the code so that it streams the upload directly to Google Storage.如果它首先将文件保存到内存中,我该如何更改代码以便将上传内容直接流式传输到 Google Storage。 The answers to similar questions didn't clarify my question.类似问题的答案并没有澄清我的问题。

Thank you谢谢

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

As noted in a comment on question and the answer by Peter, use the multipart reader directly to read the request body.正如彼得在对问题和答案的评论中所指出的,直接使用多部分阅读器来阅读请求正文。

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

Replace the // handle error comments with code that responds to the client with an appropriate error status.// handle error注释替换为以适当的错误状态响应客户端的代码。 It may be useful to log some of the errors as well.记录一些错误也可能很有用。

FormFile returns the first file for the provided form key. FormFile 返回提供的表单键的第一个文件。 FormFile calls ParseMultipartForm and ParseForm if necessary.如有必要,FormFile 会调用 ParseMultipartForm 和 ParseForm。

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

ParseMultipartForm parses a request body as multipart/form-data. ParseMultipartForm 将请求正文解析为 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.整个请求正文被解析,其文件部分的总共 maxMemory 字节存储在内存中,其余部分存储在磁盘上的临时文件中。

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

At the time of writing this, FormFile passes 32 MB as the maxMemory argument .在撰写本文时, FormFile 传递 32 MB 作为 maxMemory 参数

So you with this code you will need up to 32 MB of memory per request, plusgoogleapi.DefaultUploadChunkSize, which is currently 8 MB , as well as some amount of disk space for everything that doesn't fit in memory.因此,使用此代码,每个请求最多需要 32 MB 内存,外加googleapi.DefaultUploadChunkSize(目前为 8 MB) ,以及一些磁盘空间,用于存储不适合内存的所有内容。

So uploading will not start until the whole file has been read, but not all of it is kept in memory.因此,在读取整个文件之前不会开始上传,但并非所有文件都保存在内存中。 If that's not what you want, use Request.MultipartReader instead of ParseMultipartForm:如果这不是您想要的,请使用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.如果这是一个 multipart/form-data 或 multipart/mixed POST 请求,则 MultipartReader 返回一个 MIME 多部分阅读器,否则返回 nil 和错误。 Use this function instead of ParseMultipartForm to process the request body as a stream.使用此函数代替 ParseMultipartForm 将请求正文作为流处理。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM