【问题标题】:Java, how can you chunk pieces of a large inputstream efficiently?Java,您如何有效地分块大型输入流?
【发布时间】:2020-07-04 08:26:27
【问题描述】:

我有一个可能为 20-30mb 的输入流。我正在尝试将块作为多部分文件上传到 S3。

我有可用的内容长度和可用的输入流。考虑到记忆,我怎样才能有效地做到这一点。

我看到有人做过这样的事情,但不确定我是否完全理解:

    int contentLength = inputStreamMetadata.getContentLength();
    int partSize = 512 * 1024; // Set part size to 2 MB
    int filePosition = 0;

    ByteArrayInputStream bais = inputStreamMetadata.getInputStream();
    List<PartETag> partETags = new ArrayList<>();
    byte[] chunkedFileBytes = new byte[partSize];
    for (int i = 1; filePosition < contentLength; i++) {
      // Because the last part could be less than 5 MB, adjust the part size as needed.
      partSize = Math.min(partSize, (contentLength - filePosition));

      filePosition += bais.read(chunkedFileBytes, filePosition, partSize);

      // Create the request to upload a part.
      UploadPartRequest uploadRequest = new UploadPartRequest()
          .withBucketName(bucketName)
          .withUploadId(uploadId)
          .withKey(fileName)
          .withPartNumber(i)
          .withInputStream(new ByteArrayInputStream(chunkedFileBytes, 0, partSize))
          .withPartSize(partSize);

      UploadPartResult uploadResult = client.uploadPart(uploadRequest);
      partETags.add(uploadResult.getPartETag());
    }
}

具体这件作品:.withInputStream(new ByteArrayInputStream(bytes, 0, bytesRead))

【问题讨论】:

  • 实际上它与AWS low level API upload doc 对齐...一个区别:示例使用withFilewithFileOffset,您使用withInputStream(似乎也正确:使用来自当前加载的InputStream块)...一个 tiny 问题,我知道,是 ..最后一次迭代 (bytesRead &lt;= 0) ..但也会测试它的行为方式。
  • API 应该有一种启用分块传输模式的方法,这一切都为您完成。
  • 我更新了代码,但是 [message=Range [524288, 524288 + 179947) 超出了长度 524288 的范围,错误。我不知道为什么
  • ..但是 20-30mb 的努力(分块)值得吗!? (我在这里遇到了@Stack Overflow 的人,他们通过putObject 增加了 2GB(知道文件大小;)...stackoverflow.com/q/54379555/592355
  • 老实说,上传速度对我来说非常缓慢,只有几 mb,但不知道为什么。我原以为这些部分都是并行完成的,但实际上并不这么认为。

标签: java amazon-web-services


【解决方案1】:

对不起,我不能(轻松)测试它,但我认为你真的很接近,......只需要“修复”和“安排”你的循环!

https://stackoverflow.com/a/22128215/592355 与您的最新代码结合起来:

int partSize = 5 * 1024 * 1024; // Set part size to 5 MB
ByteArrayInputStream bais = inputStreamMetadata.getInputStream();
List<PartETag> partETags = new ArrayList<>();
byte[] buff = new byte[partSize];
int partNumber = 1;
while (true) {//!
    int readBytes = bais.read(buff);// readBytes in [-1 .. partSize]!
    if (readBytes == -1) { //EOF
        break;
    }
    // Create the request to upload a part.
    UploadPartRequest uploadRequest = new UploadPartRequest()
                .withBucketName(bucketName)
                .withUploadId(uploadId)
                .withKey(fileName)
                .withPartNumber(partNumber++)
                .withInputStream(new ByteArrayInputStream(buff, 0, readBytes))
                .withPartSize(readBytes);

    UploadPartResult uploadResult = client.uploadPart(uploadRequest);
    partETags.add(uploadResult.getPartETag());
}
// Complete the multipart upload.... 
// https://docs.aws.amazon.com/AmazonS3/latest/dev/llJavaUploadFile.html

【讨论】:

  • 谢谢!我会试试看。使用这种方法上传文件是否有任何速度优势?就像我用 10mb 做一个 putObjectReq 或这个一样,它可能需要大约相同的时间,对吧?
  • -告诉我们! :) ..但我怀疑......“上传”不是“线程/部件的数量”是瓶颈,而是你/客户的“上游”(以及到达aws的东西)......你可以实现一个“大(运行时)优势”,如果你可以“解雇并忘记”......请同时“测试”"high level" approach,尤其是(尽量避免)waitForCompletion(); 部分。
  • 我会做高级,但是处理这个输入流,我不确定这是否可能.. 我想做 asyc 上传虽然
  • 没问题 - 高层也提供upload(InputStream),但也需要“元数据”(->文件大小,你知道吗!?)
  • 所以在我上面的例子中,如果我读取的最后一个块没有退出 5MB,比如说是 500kb,那会发生什么? readBytes 会是 -1?
猜你喜欢
  • 2022-08-10
  • 2023-03-23
  • 2016-04-24
  • 2018-07-18
  • 2012-07-29
  • 2017-04-09
  • 1970-01-01
  • 1970-01-01
  • 2011-01-30
相关资源
最近更新 更多