【问题标题】:Amazon S3 multipart upload part size via lambda通过 lambda 的 Amazon S3 分段上传部分大小
【发布时间】:2023-03-14 00:09:01
【问题描述】:

我有一些 lambda 函数允许将分段上传到 Amazon S3 存储桶。它们负责创建分段上传,然后为每个分段上传创建另一个,最后一个用于完成上传。

前两个似乎工作正常(它们以 statusCode 200 响应),但最后一个失败了。在 Cloudwatch 上,我可以看到一条错误消息“您建议的上传文件小于允许的最小大小”。

这不是真的,因为我上传的文件大于文档中指定的最小 5Mb 大小。但是,我认为这个问题在每个单独的部分上传中都会发生。

为什么?因为每个部分只有 2Mb 的数据。在文档上,我可以看到除了最后一部分之外的每一部分都需要至少 5Mb 大小。但是,当我尝试上传大于 2Mb 的部分时,我收到了 CORS 错误,很可能是因为我已经超过了 6Mb 的 lambda 有效负载限制。

谁能帮我解决这个问题?下面我留下我的客户端代码,以防您在上面看到任何错误。

setLoading(true);
const file = files[0];
const size = 2000000;
const extension = file.name.substring(file.name.lastIndexOf('.'));
try {
  const multiStartResponse = await startMultiPartUpload({ fileType: extension });
  console.log(multiStartResponse);
  let part = 1;
  let parts = [];
  /* eslint-disable no-await-in-loop */
  for (let start = 0; start < file.size; start += size) {
    const chunk = file.slice(start, start + size + 1);
    const textChunk = await chunk.text();
    const partResponse = await uploadPart({
      file: textChunk,
      fileKey: multiStartResponse.data.Key,
      partNumber: part,
      uploadId: multiStartResponse.data.UploadId,
    });
    console.log(partResponse);
    parts.push({ ETag: partResponse.data.ETag, PartNumber: part });
    part++;
  }
  /* eslint-enable no-await-in-loop */
  const completeResponse = await completeMultiPartUpload({
    fileKey: multiStartResponse.data.Key,
    uploadId: multiStartResponse.data.UploadId,
    parts,
  });
  console.log(completeResponse);
} catch (e) {
  console.log(e);
} finally {
  setLoading(false);
}

【问题讨论】:

    标签: amazon-web-services amazon-s3


    【解决方案1】:

    看来通过 lambda 上传部分是根本不可能的,所以我们需要使用不同的方法。

    现在,我们的 startMultiPartUpload lambda 不仅返回一个上传 ID,还返回一堆签名 URL,由 S3 aws-sdk 类生成,使用 getSignedUrlPromise 方法,并以 'uploadPart' 作为操作,如下所示:

    const getSignedPartURL = (bucket, fileKey, uploadId, partNumber) =>
       s3.getSignedUrlPromise('uploadPart', { Bucket: bucket, Key: fileKey, UploadId: 
       uploadId, PartNumber: partNumber })
    

    另外,由于以这种方式上传零件不会返回 ETag(或者它可能会,但我无法实现),我们需要在上传每个零件后调用 S3 类的 listParts 方法以获取那些电子标签。我将在下面留下我的 React 代码:

    const uploadPart = async (url, data) => {
    try {
    // return await uploadPartToS3(url, data);
    return fetch(url, {
      method: 'PUT',
      body: data,
     }).then((e) => e.body);
    } catch (e) {
     console.error(e);
     throw new Error('Unknown error');
     }
    };
    
    // If file is bigger than 50Mb then perform a multi part upload
    const uploadMultiPart = async ({ name, size, originFileObj }, 
     updateUploadingMedia) => {
    // chunk size determines each part size. This needs to be > 5Mb
    const chunkSize = 60000000;
    let chunkStart = 0;
    const extension = name.substring(name.lastIndexOf('.'));
    const partsQuan = Math.ceil(size / chunkSize);
    // Start multi part upload. This returns both uploadId and signed urls for each 
    part.
    const startResponse = await startMultiPartUpload({
      fileType: extension,
      chunksQuan: partsQuan,
    });
    console.log('start response: ', startResponse);
    const {
      signedURLs,
      startUploadResponse: { Key, UploadId },
    } = startResponse.data;
    try {
      let promises = [];
      /* eslint-disable no-await-in-loop */
      for (let i = 0; i < partsQuan; i++) {
        // Split file into parts and upload each one to it's signed url
        const chunk = await originFileObj.slice(chunkStart, chunkStart + 
       chunkSize).arrayBuffer();
      chunkStart += chunkSize;
      promises.push(uploadPart(signedURLs[i], chunk));
      if (promises.length === 5) {
        await Promise.all(promises);
        promises = [];
      }
      console.log('UPLOAD PART RESPONSE', uploadResponse);
    }
    /* eslint-enable no-await-in-loop */
    // wait until every part is uploaded
    await allProgress({ promises, name }, (media) => {
      updateUploadingMedia(media);
    });
    
    // Get parts list to build complete request (each upload does not retrieve ETag)
    const partsList = await listParts({
      fileKey: Key,
      uploadId: UploadId,
    });
    // build parts object for complete upload
    const completeParts = partsList.data.Parts.map(({ PartNumber, ETag }) => ({
      ETag,
      PartNumber,
    }));
    // Complete multi part upload
    completeMultiPartUpload({
      fileKey: Key,
      uploadId: UploadId,
      parts: completeParts,
    });
    return Key;
    } catch (e) {
      console.error('ERROR', e);
      const abortResponse = await abortUpload({
        fileKey: Key,
        uploadId: UploadId,
      });
      console.error(abortResponse);
    }
    };
    

    抱歉,我已尽我所能逐行更正:)。

    一些注意事项:

    -我们使用 60Mb 块,因为我们的后端为大文件生成所有这些签名的 url 花费了太长时间。

    -此外,此解决方案旨在上传非常大的文件,这就是我们等待每 5 个部分的原因。

    但是,我们仍然面临上传大文件(约 35gb)的问题,因为上传 100/120 部分后,获取请求突然开始失败,并且没有更多部分上传。如果有人知道发生了什么,那就太棒了。我将其作为答案发布,因为我认为大多数人会发现这非常有用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-08-23
      • 2014-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-14
      • 2017-01-06
      • 2015-12-28
      相关资源
      最近更新 更多