AmazonS3:收到警告:S3AbortableInputStream:并非从 S3ObjectInputStream 读取所有字节,正在中止 HTTP 连接

问题描述 投票:0回答:7

这是我收到的警告:

S3AbortableInputStream:未从 S3ObjectInputStream 读取所有字节,中止 HTTP 连接。这可能是一个错误,并可能导致次优行为。通过范围 GET 仅请求您需要的字节,或在使用后耗尽输入流。

我尝试对资源使用 try,但 S3ObjectInputStream 似乎没有通过此方法关闭。

 try (S3Object s3object = s3Client.getObject(new GetObjectRequest(bucket, key));
      S3ObjectInputStream s3ObjectInputStream = s3object.getObjectContent();
      BufferedReader reader = new BufferedReader(new InputStreamReader(s3ObjectInputStream, StandardCharsets.UTF_8));
    ){
  //some code here blah blah blah
 }

我还尝试了下面的代码并显式关闭,但这也不起作用:

S3Object s3object = s3Client.getObject(new GetObjectRequest(bucket, key));
S3ObjectInputStream s3ObjectInputStream = s3object.getObjectContent();

try (BufferedReader reader = new BufferedReader(new InputStreamReader(s3ObjectInputStream, StandardCharsets.UTF_8));
){
     //some code here blah blah
     s3ObjectInputStream.close();
     s3object.close();
}

如有任何帮助,我们将不胜感激。

PS:我只从S3读取文件的两行,并且文件有更多数据。

amazon-s3 aws-sdk aws-java-sdk
7个回答
34
投票

通过其他媒体得到答案。在此分享:

该警告表明您在未读取整个文件的情况下调用了 close()。这是有问题的,因为 S3 仍在尝试发送数据,而您使连接处于悲伤状态。

这里有两个选项:

  1. 从输入流中读取其余数据,以便可以重用连接。
  2. 调用s3ObjectInputStream.abort()关闭连接,不读取数据。连接不会被重用,因此您在下一个请求重新创建连接时会受到一些性能影响。如果需要很长时间才能阅读文件的其余部分,这可能是值得的。

6
投票

Chirag Sejpal 的答案的以下选项 #1 我使用以下语句来耗尽 S3AbortableInputStream 以确保可以重用连接:

com.amazonaws.util.IOUtils.drainInputStream(s3ObjectInputStream);
 

4
投票

我遇到了同样的问题,下面的课程帮助了我

@Data
@AllArgsConstructor
public class S3ObjectClosable implements Closeable {
    private final S3Object s3Object;

    @Override
    public void close() throws IOException {
        s3Object.getObjectContent().abort();
        s3Object.close();
    }
}

现在您可以毫无警告地使用

try (final var s3ObjectClosable = new S3ObjectClosable(s3Client.getObject(bucket, key))) {

//same code

}


2
投票

要在 Chirag Sejpal 的答案中添加一个示例(详细说明选项 #1),可以使用以下命令在关闭输入流之前从输入流中读取其余数据:

S3Object s3object = s3Client.getObject(new GetObjectRequest(bucket, key));

try (S3ObjectInputStream s3ObjectInputStream = s3object.getObjectContent()) {
  try {
    // Read from stream as necessary
  } catch (Exception e) {
    // Handle exceptions as necessary
  } finally {
    while (s3ObjectInputStream != null && s3ObjectInputStream.read() != -1) {
      // Read the rest of the stream
    }
  }

  // The stream will be closed automatically by the try-with-resources statement
}

0
投票

我遇到了同样的错误。

正如其他人指出的,lambda 中的 /tmp 空间限制为 512 MB。 如果 lambda 上下文被重新用于新的调用,那么 /tmp 空间已经是半满了。

因此,当读取 S3 对象并将所有文件写入 /tmp 目录时(正如我所做的那样), 我在两者之间的某个地方用完了磁盘空间。 Lambda 因错误退出,但并非读取 S3ObjectInputStream 中的所有字节

因此,需要记住两件事:

1) 如果第一次执行导致问题,请吝惜 /tmp 空间。 我们只有 512 MB

2)如果第二次执行导致问题,那么可以通过攻击根本问题来解决。 无法删除 /tmp 文件夹。 所以,执行完成后删除/tmp文件夹下的所有文件。

在java中,这是我所做的,成功解决了问题。

public String handleRequest(Map < String, String > keyValuePairs, Context lambdaContext) {
  try {
    // All work here
  } catch (Exception e) {
    logger.error("Error {}", e.toString());
    return "Error";
  } finally {
    deleteAllFilesInTmpDir();
  }
}
private void deleteAllFilesInTmpDir() {
  Path path = java.nio.file.Paths.get(File.separator, "tmp", File.separator);
  try {
    if (Files.exists(path)) {
      deleteDir(path.toFile());
      logger.info("Successfully cleaned up the tmp directory");
    }
  } catch (Exception ex) {
    logger.error("Unable to clean up the tmp directory");
  }
}
public void deleteDir(File dir) {
  File[] files = dir.listFiles();
  if (files != null) {
    for (final File file: files) {
      deleteDir(file);
    }
  }
  dir.delete();
}


0
投票

这是我的解决方案。我正在使用 Spring Boot 2.4.3

创建一个 amazon s3 客户端

AmazonS3 amazonS3Client = AmazonS3ClientBuilder
                .standard()
                .withRegion("your-region")
                .withCredentials(
                        new AWSStaticCredentialsProvider(
                            new BasicAWSCredentials("your-access-key", "your-secret-access-key")))
                .build();

创建一个亚马逊转账客户端

TransferManager transferManagerClient = TransferManagerBuilder.standard()
                .withS3Client(amazonS3Client)
                .build();

/tmp/{your-s3-key}中创建一个临时文件,以便我们可以将下载的文件放入此文件中。

File file = new File(System.getProperty("java.io.tmpdir"), "your-s3-key"); 

try {
    file.createNewFile(); // Create temporary file
} catch (IOException e) {
    e.printStackTrace();
}

file.mkdirs();  // Create the directory of the temporary file

然后,我们使用 transfer manager client

从 s3 下载文件
// Note that in this line the s3 file downloaded has been transferred in to the temporary file that we created
Download download = transferManagerClient.download(
               new GetObjectRequest("your-s3-bucket-name", "your-s3-key"), file); 

// This line blocks the thread until the download is finished
download.waitForCompletion();  

现在 s3 文件已成功转移到我们创建的临时文件中。我们可以获取临时文件的InputStream。

InputStream input = new DataInputStream(new FileInputStream(file));

因为不再需要临时文件,我们只需将其删除即可。

file.delete();

0
投票

我通过合并 s3Object.getObjectContent().abort() 方法解决了警告问题。

有关我的场景的更多详细信息:

我的目标是压缩超过 600KB 的图像。获取 S3Object 后,我检查并压缩大小是否> 600KB。然而,对于尺寸<= 600KB, warnings occurred as Try-with-resources closed the connection without calling abort(). 使用 abort() 解决了问题

这里你可能会想到一个问题,比如为什么即使我没有请求内容也需要中止,但是当你获取 s3Object 时,它看起来也开始获取内容,因此当我们不需要内容时我们需要中止

try (S3Object s3Object = awsService.getS3Object(s3Bucket, s3PublicPath)) {

  long fileSizeInKB = s3Object.getObjectMetadata().getContentLength() / 1024;
  if (fileSizeInKB > 600) { // code for image compress or resize}

  // after 

  else { s3Object.getObjectContent().abort(); }
}
© www.soinside.com 2019 - 2024. All rights reserved.