从 Java SDK 上传 S3 比 AWS CLI 慢得多

wrs*_*der 3 amazon-s3 amazon-web-services aws-sdk

与 AWS CLI 相比,在保持一切不变的情况下,使用 Java SDK 上传相对较小的文件 (15 MB) 的速度要慢得多:相同的笔记本电脑、相同的 AWS 账户、相同的区域。

我的代码或多或少遵循与AWS 文档相同的基本模式

// inputStream is ByteArrayInputStream, all in memory 
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType("text/plain");
metadata.setContentLength(contentLength);

PutObjectRequest request = new PutObjectRequest(bucketName, s3keyName, inputStream, metadata);
AmazonS3 s3Client = AmazonS3ClientBuilder.standard().build();
s3Client.putObject(request);
Run Code Online (Sandbox Code Playgroud)

性能差异:

  • AWS CLI ( aws s3 cp ...) 大约需要 15 秒
  • Java SDK 需要一分多钟的时间

std*_*bar 5

我尝试了几种不同的选择。我什至生成了一个预先签名的 URL 并使用了curl,但对我来说,对于一个 15MB 的文件,一切都需要大约 15-16 秒,而 CLI 则需要 9 秒。正如@Mark B 指出的那样,虽然 TransferManager 是关键。从这段代码开始,但通过告诉代码我想要多少线程,withMultipartUploadThreshold我的时间减少到了大约 6.5 秒。

注意三件事:

  1. 我让图书馆读取一个文件。当我使用 ByteArrayInputStream 时,程序似乎没有多线程,而且我仍然在 15 秒。让 AWS 库读取文件似乎让程序本身分割了文件。
  2. withMultipartUploadThreshold很棘手。如果我给它 15MB,则没有任何改善。如果我给它 5MB,则没有任何改善。但在 1MB 时就有了显着的改进。但我不知道下限在哪里。
  3. 我没有设置内容类型。这必须在单独的 S3 调用中完成。

我还有另外 4 个示例,如果您也愿意的话,它们效果不太好。

public class HighLevelMultipartUpload {

    public static void main(String[] args) throws Exception {
        Regions clientRegion = Regions.US_EAST_2;
        String bucketName = "<my-bucket>";
        String fileObjKeyName = "file.txt";
        String fileName = "/tmp/file.txt";

        long startTime = System.currentTimeMillis();

        // this made it slower
        // String fileContent = Files.readString(Path.of(fileName));

        try {
            AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
                    .withRegion(clientRegion)
                    .withCredentials(new ProfileCredentialsProvider())
                    .build();
            TransferManager tm = TransferManagerBuilder.standard().withMultipartUploadThreshold(1024L*1024)
                    .withS3Client(s3Client)
                    .build();

            // TransferManager processes all transfers asynchronously,
            // so this call returns immediately.
            Upload upload = tm.upload(bucketName, fileObjKeyName, new File(fileName));

            // used if using the file and supplying metadata
            // Upload upload = tm.upload(bucketName, fileObjKeyName, new ByteArrayInputStream(fileContent.getBytes()), metadata);
            System.out.println("Object upload started");

            // Optionally, wait for the upload to finish before continuing.
            upload.waitForCompletion();
            System.out.println("Object upload complete");
        } catch (SdkClientException e) {

            e.printStackTrace();
        }

        System.out.println( "run took " + (System.currentTimeMillis() - startTime) + "ms");
        System.exit(0);
    }
}
Run Code Online (Sandbox Code Playgroud)