我想直接从前端通过后端将文件上传到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()
}
答案 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
注释替换为以适当的错误状态响应客户端的代码。记录一些错误也可能很有用。