大型多部分文件直接上传到AWS S3

问题描述 投票:0回答:1

我有一个托管在 nginx(反向代理 - proxy_pass 指令)后面的 go-chi (golang) Web 应用程序,它接受文件上传,然后上传到 AWS S3。

带有文件的请求是通过带有

content-type
的 multipart/form-data 的 POST 请求发送的。

应用程序然后接收请求并创建一个

multipart.file
类型,根据我的理解,这个整个文件都分配在内存中。

我愿意接受 5GB - 10GB 范围内的文件,并且无法增加此大小的服务器内存,因为我只有 2GB。

S3 具有分段上传方法,允许将文件的部分内容上传到 S3。

我已经在下面的

go
中实现了这个分段文件上传,我面临的问题是我觉得有重复的工作,因为go-http库已经在部分分配内存以使
*multipart.file
类型本质上做我的事情我在下面做。

  1. 有没有办法将ParseMultipartForm()

    go-http
    库创建的这些部分文件直接上传到S3以避免整个文件在内存中的分配?(上传文件流为它进来了)

  2. 避免在go-http中解析和分配

    mutlipart.file
    并将其直接加载到磁盘,然后使用multipart s3方法上传文件部分会更好吗?尽管我相信我仍然需要在内存中流式传输整个文件才能执行此操作,但由于我没有足够的内存,这将无法工作

  3. Nginx 在将其传递给 Web 应用程序之前是否也会解析并分离该文件?

我在下面实现了 S3 分段上传的轻型 POC。

partBufSize := int64(10000000)
byteOffset := int64(0)

for { 
    remainingStreamBytes := uploadSize - bytesOffset
    if remainingStreamBytes < partBufSize {
        partBufSize = remainingStreamBytes
    }

    _, _ = uploadedFile.Seek(bytesOffset, io.SeekStart)

    filePartBuffer := make([]byte, partBufSize)
    _, err := uploadedFile.Read(filePartBuffer)
    if err != nil {
        if err == io.EOF {
            break
        }
        log.Error().Msg(err.Error())
    }

    uploadPartRes, err := s3Client.UpParts(&aws.PartObjectInput{
        Key:      uploadPartsRequest.Key,
        Body:     bytes.NewReader(filePartBuffer),
        PartNum:  aws.Int64(int64(count)),
        UpId:     uploadPartsRequest.UploadId,
        PartSize: aws.Int64(partBufSize),
    })
    if err != nil {
        log.Error().Msg(err.Error())
    }
    bytesOffset = bytesOffset + partBufSize

    compParts = append(compParts, &s3.CompletedPart{
        ETag:       uploadPartRes.ETag,
        PartNumber: aws.Int64(int64(count)),
    })
    count++
}
amazon-web-services go nginx amazon-s3 go-http
1个回答
0
投票

应用程序然后接收请求并创建一个 multipart.file 类型,根据我的理解,整个文件都分配在内存中。

我不太明白这一点。如果这是您编码的方式,那么就不要这样做:Go 提供了

mime/multipart.Reader
,可用于读取
multipart/form-data
编码的负载。
要做到这一点,你可以像这样滚动:

  1. 获取
    Content-Type
    标头字段的值,并使用
    mime.ParseMediaType
    函数解析它,以获取“边界”的值——用于分隔多部分有效负载中各部分的字符串。
  2. 从请求正文和边界字符串中创建一个
    multipart.Reader
  3. 迭代各个部分;在每个部分,您都会得到另一个
    io.Reader
    ,它将提供该部分的字节。对这些字节执行任何您想要的操作 - 可能使用
    io.Copy
    io.CopyN
    将它们放入活动的 POST 请求中。

这样您就可以控制要提供的确切缓冲量。

© www.soinside.com 2019 - 2024. All rights reserved.