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

AWS多部分上传API的挑战:您建议的上传小于允许的最小大小

沈飞翼
2023-03-14

我在Java读取拼花文件时构建了一个XML字符串。该xml字符串需要上传到S3存储桶。

拼花文件最多可以有200万条记录。

将XML文件上载到S3的一种方法是使用AWS的多部分上载API,方法如下:

构建XML字符串后,将该字符串转换为inputstream,并在UploadPart Request对象中使用它来调用s3Client.uploadPart()方法。

提交人:

  1. https://docs.aws.amazon.com/AmazonS3/latest/dev/uploadobjusingmpu.html
  2. https://docs.aws.amazon.com/AmazonS3/latest/dev/llJavaUploadFile.html

代码看起来像这样:

long partSize = 5242880L;
 AmazonS3 s3Client = (AmazonS3)((AmazonS3ClientBuilder)AmazonS3ClientBuilder.standard().withRegion(clientRegion)).build(); 
List<PartETag> partETags = new ArrayList();
long filePosition = 0L;

        for(int i = 1; filePosition < contentLength; ++i) {
            partSize = Math.min(partSize, contentLength - filePosition);
            DLFUtil.logInfo(" Part Size - " + partSize);
            UploadPartRequest uploadRequest = (new UploadPartRequest()).withBucketName(objectDetails.getBucketName()).withKey(objectDetails.getS3Key()).withUploadId(objectDetails.getUploadId()).withPartNumber(i).withInputStream(body).withPartSize(partSize);
            UploadPartResult uploadResult = s3Client.uploadPart(uploadRequest);
            DLFUtil.logInfo("Uploaded Part " + i);
            partETags.add(uploadResult.getPartETag());
            filePosition += partSize;
            DLFUtil.logInfo("filePosition " + filePosition);
        }

        CompleteMultipartUploadRequest compRequest = new CompleteMultipartUploadRequest(objectDetails.getBucketName(), objectDetails.getS3Key(), objectDetails.getUploadId(), partETags);
        s3Client.completeMultipartUpload(compRequest);

面临的挑战是,由于parquet文件可能有200万条记录,所以当构建xml字符串时,它将是内存中的一个巨大的字符串(字符串大小可能以GBs为单位)。为了避免在内存中有一个巨大的字符串,我想到了分块构建字符串-

但是,由于这样做,我遇到了错误:“您建议的上传小于允许的最小大小”

我收到此错误的原因是,当我对字符串的一部分进行分段上传时,最后一部分将小于5 MB。对于另一个块的第二次分段上传,最后一部分将再次小于5 MB。当 AWS 尝试使用完整的多部分Upload() 组装所有部件时,它预计只有一个部分小于 5 MB,因为在典型情况下,输入流将具有整个字符串或文件,只有最后一个部分小于 5 MB。

我正在尝试找出一种方法来上传xml字符串,而不必一次将整个字符串保存在内存中。如果有什么建议,请告诉我。

共有1个答案

乔俊才
2023-03-14

我与AWS支持人员聊天,他们告诉我,多部分上传API的设计方式是,它只能接受小于5MB的最后一部分。因此,我们需要上传部件以满足这些要求。

为了解决这个问题,我们所做的是——只要剩余的部分大小小于5MB,我们就将输入流转换回字符串并返回字符串。构建字符串的函数将接收该字符串,并继续从那里构建字符串。

例如,我们假设整个字符串将在 701 MB 左右。另外,假设我们有一个函数 buildString(), 它生成需要上传的字符串。该函数从头开始构建字符串,当字符串超过 100 MB 时,我们会对该字符串执行分段上传(如果对象将超过 100 MB,AWS 建议以分段方式上传对象)。假设构建的字符串为 101 MB,我们为该字符串执行分段上传。AWS 的分段上传 API 预计分段对象应以 5 MB 的分段上传。因此,对于 101 MB 的字符串,将上传 5 MB 的 20 个部分,对于剩余的 1 MB 部分 - 我们将它转换回字符串并将其发送回 buildString() 函数,而不是上传它。生成字符串() 将采用该字符串并继续从那里生成字符串。当字符串长度再次超过 100 MB 时,将重复相同的步骤。这样,上传的每个部分将正好为5 MB。这个难题唯一剩下的部分是,当buildString()完成字符串的构建并上传剩余的字符串时,我们不需要返回小于5 MB的字符串。相反,这应该被上传,因为这确实是701 MB字符串的最后一部分,小于5 MB。

下表给出了一个更好的想法:表中显示了每次多部分上传的字符串长度

使用此解决方案,上传多部分的方法如下所示:

public String uploadMultipart(boolean isLastPart, InputStream body, long contentLength) throws IOException {
        long partSize = 5242880L;

        long filePosition = 0L;

        String remainderString = null;
        int i;
        for (i = partNumber; filePosition < contentLength; ++i) {

            if ((contentLength - filePosition) < partSize && !isLastPart) {

                /**
                 * Do not publish the part which is less than 5 MB. Instead, convert it to string and return it back.
                 * The reason for that is - AWS only wants one last part to be less than 5 MB.
                 * However, with our custom-built solution to upload the parts while building the string, we would end up with multiple parts less than 5 MB.
                 * To avoid that, we will not publish the part which is less than 5 MB. We will return the string and continue building the string from there.
                 * Only if isLastPart is true (which indicates that we have completed building the string), we will not return the remaining string and rather upload it as
                 * that will indeed be the last part less than 5 MB as expected by AWS.
                 */
                remainderString = getStringFromInputStream(body);
                break;

            } else {

                partSize = Math.min(partSize, (contentLength - filePosition));

                UploadPartRequest uploadRequest = (new UploadPartRequest()).withBucketName(getBucketName()).withKey(getS3Key()).
                        withUploadId(getUploadId()).withPartNumber(i).withInputStream(body).withPartSize(partSize);
                UploadPartResult uploadResult = s3Client.uploadPart(uploadRequest);
                partETags.add(uploadResult.getPartETag());
                filePosition += partSize;
            }
        }
        partNumber = i;
        return remainderString;
    }

请注意,在这种情况下,对于每个多部分上传,UploadPart Request对象中设置的存储桶名称、S3键和上传ID应该是相同的。只有这样,AWS才会知道这些部分与同一个对象相关联,并且最终会将它们组装在一起,以便在最后调用AWS的完成多部分上传()函数时拥有我们的单个701 MB对象。

 类似资料:
  • 当我想上传图片到我的亚马逊s3存储桶时,我遇到了一些问题 我正试着上传一张238 KB的jpg图片。我在代码中放了一个try/catch来检查错误是什么。我总是得到这个错误: 您建议的上传小于允许的最小大小 我也用1MB和2MB的图片试过,同样的错误...。 这是我的代码: (我已经更改了桶、键和图像链接。) 以前有人有过这个吗?在互联网上搜索对我没有太大帮助。 还搜索更改最小上传大小也没有提供太

  • 您可以使用基于浏览器的上传(不是多部分上传)中的内容长度范围字段来限制文件大小:http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-HTTPPOSTConstructPolicy.html 你怎么能在多部分上传中限制它?我正在使用EvaporateJS。

  • 有没有办法,如何上传比5MB更小的文件?多部分上传要求块的大小大于5MB(不包括最后一个)。但是我正在处理32KB的块。 有没有一种方法可以上传较小的块,或者让我保存我的块,直到它们达到5MB大小,然后使用多部分上传? 感谢解答!

  • 我正在尝试将图像文件从临时php文件存储路径上传到S3。文件大小约为100 kb,最大为500 kb。 当上传发生时,我得到这个错误: 我不认为我需要在这里进行分段上传,因为文件不是太大。此外,上传从我的本地系统到 S3,但是当我在 AWS 上部署 PHP 实例时,上传会给我一个错误。任何帮助将不胜感激。 谢谢你

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

  • 问题内容: 我有一个无法访问的PC上托管的网站。我有一个上传表单,允许人们上传最大30MB的mp3文件。我的服务器端脚本是用PHP完成的。 每次尝试上传文件时,都会收到一条错误消息,声称文件超出了允许的最大大小,因此我需要增加大小。我在网络上的研究建议更改我无权访问的文件,这样将无法正常工作。其他人建议我将自定义文件添加到我的根目录中,该文件无效。还有其他建议吗? 问题答案: 您需要设置的值,并在