微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Java 文件上传到 S3 - 多部分应该加快速度吗?

如何解决Java 文件上传到 S3 - 多部分应该加快速度吗?

我们使用 Java 8 并使用 AWS SDK 以编程方式将文件上传到 AWS S3。对于上传文件 (>100MB),我们了解到首选使用方法是分段上传。我们尝试过,但似乎并没有加快速度,上传时间几乎与不使用分段上传相同。更糟糕的是,我们甚至遇到了内存不足的错误,说堆空间不够。

问题:

  1. 使用分段上传真的可以加快上传速度吗?如果没有,那为什么要使用它?
  2. 为什么使用分段上传比不使用更快地消耗内存?它会同时上传所有部分吗?

我们使用的代码见下文:

private static void uploadFiletoS3UsingBase64(String bucketName,String region,String accessKey,String secretKey,String fileBase64String,String s3ObjectKeyName) {
    
    byte[] bI = org.apache.commons.codec.binary.Base64.decodeBase64((fileBase64String.substring(fileBase64String.indexOf(",")+1)).getBytes());
    InputStream fis = new ByteArrayInputStream(bI);
    
    long start = System.currentTimeMillis();
    AmazonS3 s3Client = null;
    TransferManager tm = null;

    try {

        s3Client = AmazonS3ClientBuilder.standard().withRegion(region)
                .withCredentials(new AWsstaticCredentialsProvider(new BasicAWSCredentials(accessKey,secretKey)))
                .build();
        
        tm = TransferManagerBuilder.standard()
                  .withS3Client(s3Client)
                  .withMultipartUploadThreshold((long) (50* 1024 * 1025))
                  .build();

        ObjectMetadata Metadata = new ObjectMetadata();
        Metadata.setHeader(Headers.STORAGE_CLASS,StorageClass.Standard);
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName,s3ObjectKeyName,fis,Metadata).withSSEAwsKeyManagementParams(new SSEAwsKeyManagementParams());
        
        Upload upload = tm.upload(putObjectRequest);

        // Optionally,wait for the upload to finish before continuing.
        upload.waitForCompletion();

        long end = System.currentTimeMillis();
        long duration = (end - start)/1000;
        
        // Log status
        System.out.println("Successul upload in S3 multipart. Duration = " + duration);
    } catch (Exception e) {
        e.printstacktrace();
    } finally {
        if (s3Client != null)
            s3Client.shutdown();
        if (tm != null)
            tm.shutdownNow();
    }

}

解决方法

如果您同时上传多个部分,使用 multipart 只会加快上传速度。

在您的代码中,您正在设置 withMultipartUploadThreshold。如果您的上传大小大于该阈值,那么您应该观察不同部分的并发上传。如果不是,则应仅使用一个上传连接。您是说您有 >100 MB 的文件,并且在您的代码中,您有 50 * 1024 * 1025 = 52 480 000 字节作为分段上传阈值,因此应该同时上传该文件的各个部分。

但是,如果您的上传吞吐量受到网络速度的限制,则吞吐量不会有任何增加。这可能是您没有观察到任何速度增加的原因。

还有其他使用 multipart 的原因,因为它也被推荐用于容错原因。此外,它的最大尺寸比单次上传更大。

有关详细信息,请参阅documentation

分段上传允许您将单个对象作为一组上传 部分。每个部分都是对象数据的连续部分。你可以 以任何顺序独立上传这些对象部分。如果 任何部分传输失败,您可以重新传输该部分而无需 影响其他部分。上传对象的所有部分后, Amazon S3 组装这些部分并创建对象。一般来说, 当您的对象大小达到 100 MB 时,您应该考虑使用 分段上传而不是一次上传对象 操作。

使用分段上传具有以下优点:

  • 提高吞吐量 - 您可以并行上传部分以提高吞吐量。

  • 从任何网络问题中快速恢复 - 较小的部件尺寸可最大限度地减少由于网络而重新启动失败上传的影响 错误。

  • 暂停和恢复对象上传 - 您可以随着时间的推移上传对象部分。发起分段上传后,没有过期;你 必须明确完成或停止分段上传。

  • 在知道最终对象大小之前开始上传 - 您可以在创建对象时上传。

我们建议您通过以下方式使用分段上传:

  • 如果您通过稳定的高带宽网络上传大型对象,请使用分段上传以最大限度地利用您的可用资源 通过为多线程并行上传对象部分来增加带宽 性能。

  • 如果您通过不稳定的网络上传,请使用分段上传避免上传重新启动,从而提高对网络错误的恢复能力。 使用分段上传时,您需要重新尝试仅上传分段 在上传过程中被中断。你不需要重新启动 从头开始上传您的对象。

,

eis 的回答很好。虽然你仍然应该采取一些行动:

  • String.getBytes(StandardCharsets.US_ASCII)ISO_8859_1 防止使用成本更高的编码,如 UTF-8。如果平台编码为 UTF-16LE,则数据甚至会损坏(0x00 字节)。
  • 标准 java Base64 有一些可能有效的解码器/编码器。它可以在字符串上工作。但是,请检查正确的处理方式(行尾)。
  • try-with-resources 也会在出现异常/内部返回的情况下关闭。
  • ByteArrayInputStream 没有关闭,这是更好的风格(更容易垃圾收集?)。
  • 您可以将 ExecutorFactory 设置为线程池工厂,以限制全局线程数。

所以

byte[] bI = Base64.getDecoder().decode(
        fileBase64String.substring(fileBase64String.indexOf(',') + 1));
try (InputStream fis = new ByteArrayInputStream(bI)) {
    ...
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。