Uppy Companion 不适用于超过 5GB 的文件,并使用 Multipart S3 上传

Uppy Companion doesn't work for > 5GB files with Multipart S3 uploads

我们的应用程序允许我们的客户上传大文件。文件存储在 AWS/S3 上,我们使用 Uppy 进行上传,并将其 dockerize 以在 kubernetes 部署下使用,我们可以增加实例的数量。

它运行良好,但我们注意到所有大于 5GB 的上传都失败了。我知道 uppy 有一个用于 AWS 分段上传的插件,但即使在创建容器镜像期间安装,结果也是一样的。

这是我们的 Dockerfile。有没有人通过 uppy 成功地将大于 5GB 的文件上传到 S3?有什么我们遗漏的吗?

FROM node:alpine AS companion
RUN yarn global add @uppy/companion@3.0.1
RUN yarn global add @uppy/aws-s3-multipart
ARG UPPY_COMPANION_DOMAIN=[...redacted..]
ARG UPPY_AWS_BUCKET=[...redacted..]


ENV COMPANION_SECRET=[...redacted..]
ENV COMPANION_PREAUTH_SECRET=[...redacted..]
ENV COMPANION_DOMAIN=${UPPY_COMPANION_DOMAIN}
ENV COMPANION_PROTOCOL="https"
ENV COMPANION_DATADIR="COMPANION_DATA"
# ENV COMPANION_HIDE_WELCOME="true"
# ENV COMPANION_HIDE_METRICS="true"
ENV COMPANION_CLIENT_ORIGINS=[...redacted..]
ENV COMPANION_AWS_KEY=[...redacted..]
ENV COMPANION_AWS_SECRET=[...redacted..]
ENV COMPANION_AWS_BUCKET=${UPPY_AWS_BUCKET}
ENV COMPANION_AWS_REGION="us-east-2"
ENV COMPANION_AWS_USE_ACCELERATE_ENDPOINT="true"
ENV COMPANION_AWS_EXPIRES="3600"
ENV COMPANION_AWS_ACL="public-read"
# We don't need to store data for just S3 uploads, but Uppy throws unless this dir exists.
RUN mkdir COMPANION_DATA

CMD ["companion"]

EXPOSE 3020

编辑:

我确定我有:

uppy.use(AwsS3Multipart, {
  limit: 5,
  companionUrl: '<our uppy url',
})

它仍然不起作用 - 我看到在网络选项卡上发送了 9GB 文件的所有块,但是一旦达到 100% - uppy 就会抛出错误“无法 post”(到我们的 S3 url) 就是这样。失败。

有人遇到过这种情况吗?上传正常,直到 100%,然后最后一个块 HTTP error 413,导致整个上传失败。

谢谢!

在单个 PUT 操作中的 AWS S3 服务中,您可以上传最大 5 GB 的单个对象。

要将大于 5GB 的文件上传到 S3,您需要使用分段上传 S3 API,以及 AwsS3Multipart Uppy API.

检查您的上传代码以了解您是否正确使用 AWSS3Multipart,例如正确设置限制,在这种情况下,建议限制在 5 到 15 之间。

import AwsS3Multipart from '@uppy/aws-s3-multipart'

uppy.use(AwsS3Multipart, {
  limit: 5,
  companionUrl: 'https://uppy-companion.myapp.net/',
})

此外,请在 Github Uploading a large >5GB file to S3 errors out #1945

上查看此问题

在这里,我从我的存储库中添加了一些代码示例,它们将帮助您了解使用 BUSBOY 包将数据流式传输到 S3 存储桶的流程。另外,我在这里添加了参考链接,供您获取我正在使用的包的详细信息。

https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-s3/index.html

https://www.npmjs.com/package/busboy

export const uploadStreamFile = async (req: Request, res: Response) => {
    const busboy = new Busboy({ headers: req.headers });
    const streamResponse = await busboyStream(busboy, req);
    const uploadResponse = await s3FileUpload(streamResponse.data.buffer);
    return res.send(uploadResponse);
};

const busboyStream = async (busboy: any, req: Request): Promise<any> {
     return new Promise((resolve, reject) => {
      try {
        const fileData: any[] = [];
        let fileBuffer: Buffer;
        busboy.on('file', async (fieldName: any, file: any, fileName: any, encoding: any, mimetype: any) => {
          // ! File is missing in the request
          if (!fileName)
            reject("File not found!");

          let totalBytes: number = 0;
          file.on('data', (chunk: any) => {
            fileData.push(chunk);
            // ! given code is only for logging purpose
            // TODO will remove once project is live
            totalBytes += chunk.length;
            console.log('File [' + fieldName + '] got ' + chunk.length + ' bytes');
          });

          file.on('error', (err: any) => {
            reject(err);
          });

          file.on('end', () => {
            fileBuffer = Buffer.concat(fileData);
          });
        });

        // ? Haa, finally file parsing wen't well
        busboy.on('finish', () => {
          const responseData: ResponseDto = {
            status: true, message: "File parsing done", data: {
              buffer: fileBuffer,
              metaData
            }
          };
          resolve(responseData)
          console.log('Done parsing data! -> File uploaded');
        });
        req.pipe(busboy);
      } catch (error) {
        reject(error);
      }

    });
  }

const s3FileUpload = async (fileData: any): Promise<ResponseDto> {
    try {
      const params: any = {
        Bucket: <BUCKET_NAME>,
        Key: <path>,
        Body: fileData,
        ContentType: <content_type>,
        ServerSideEncryption: "AES256",
      }; 
      const command = new PutObjectCommand(params);
      const uploadResponse: any = await this.S3.send(command);
      return { status: true, message: "File uploaded successfully", data: uploadResponse };
    } catch (error) {
      const responseData = { status: false, message: "Monitor connection failed, please contact tech support!", error: error.message };
      return responseData;
    }
  }