当前位置: 首页 > 知识库问答 >
问题:

通过 lambda 的 Amazon S3 分段上传段大小

嵇光临
2023-03-14

我有几个lambda函数,可以将多部分上传到Amazon S3存储桶。它们负责创建多部分上传,然后为每个部分上传创建另一个,最后一个用于完成上传。

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

事实并非如此,因为我上传的文件大于文档上指定的最小大小5Mb。但是,我认为问题发生在每个部分上传中。

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

有人能帮我吗?下面我留下了我的客户端代码,以防你能看到任何错误。

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

共有1个答案

汝飞
2023-03-14

似乎通过 lambda 上传部件根本不可能,因此我们需要使用不同的方法。

现在,我们的启动多部分上传 lambda 不仅返回一个上传 ID,还返回一堆签名的 URL,这些签名 URL 是使用 S3 aws 开发工具包类生成的,使用 getSignedUrlPromise 方法和“上传分段”作为操作,如下所示:

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

此外,由于以这种方式上传一个部件不会返回ETag(或者可能会返回,但我就是无法实现),我们需要在上传每个部件后调用S3类上的listParts方法,以获取这些ETag。我将在下面留下我的反应代码:

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个片段后,fetch请求突然开始失败,并且没有更多的片段上传。如果有人知道发生了什么,那将是惊人的。我发布这个作为答案,因为我认为大多数人会发现这非常有用。

 类似资料:
  • 我正在尝试使用放心的分段上传方法上传csv文件。在 swagger API 文档中,请求正文如下所示,用于分段 csv 上传。 我的代码是这样的。 这在IntelliJ的想法中工作,没有任何问题。但是,当我使用maven将代码打包到jar中并运行它时,它会返回fileNotes异常。 如我所见,唯一的解决方案是使用java文件input stream,但既然swagger说要使用文件,那就不可能使

  • 问题内容: 由于迁移到Swift 3,我发现很难编译使用Alamofire的项目。 上载multipartFormData时发生问题: 对成员’upload(_:to:method:headers :)的含糊引用 任何帮助,不胜感激,在此先感谢! 解决: 这就是应该在Swift 3中实现上传方法的方式 问题答案: 例如,在 Swift 3中* 使用 Alamofire 4.0.0 : * (确保您

  • 主要内容:Maven依赖关系,HttpClient分段上传请求示例在本教程中,我们将演示如何使用Apache HttpClient 4.5进行分段上传http请求。 Maven依赖关系 我们使用maven来管理依赖关系,并使用Apache HttpClient 4.5版本。 将以下依赖项添加到您的项目中。 pom.xml 文件的内容如下 - HttpClient分段上传请求示例 在这个例子中,我们将演示如何使用HttpClient 4.5来分段上传文件。 我们使

  • 我正在尝试使用 AWS PHP 开发工具包将一个 30 GB 的文件上传到亚马逊 S3。 它适用于最大7GB的文件。上传30 GB文件时,脚本运行约2小时后,我收到以下错误: 将部件上载到多部件上载时发生异常。以下部分出错:-第5部分:在上执行“UploadPart”时出错https://s3.amazonaws.com/Server3-27-SEP-2015/S3_www_1443369605.

  • 我编写了以下代码来使用多部分上传(每个部分大于5 MB)将集合字符串上传到S3,但当我从S3下载回来时,它只包含来自一个部分的字符串。 我按以下顺序调用上述代码。 创建低级多部分上传对象 循环上传零件 呼叫完成上传

  • 根据Amazon多部分上传文档,除最后一部分外,每个部分的大小必须至少为5MB。http://docs.aws.amazon.com/AmazonS3/latest/API/mpUploadUploadPart.html 问题是我如何通过多部分上传api将小于5MB的文件上传到AWS S3存储桶。我问这个问题的原因是,我想在上传到S3时对所有文件使用多部分上传API