【问题标题】:AmazonS3: Getting warning: S3AbortableInputStream:Not all bytes were read from the S3ObjectInputStream, aborting HTTP connectionAmazonS3:收到警告:S3AbortableInputStream:并非所有字节都从 S3ObjectInputStream 中读取,正在中止 HTTP 连接
【发布时间】:2018-01-19 07:41:40
【问题描述】:

这是我收到的警告:

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


    【解决方案1】:

    这是我的解决方案。我正在使用 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();
    

    【讨论】:

      【解决方案2】:

      按照 Chirag Sejpal 回答的选项 #1,我使用以下语句排空 S3AbortableInputStream 以确保可以重用连接:

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

      【讨论】:

        【解决方案3】:

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

        @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
        
        }
        
        

        【讨论】:

          【解决方案4】:

          我遇到了同样的错误。

          正如其他人指出的那样,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();
          }

          【讨论】:

            【解决方案5】:

            要在 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
            }
            

            【讨论】:

            • 如果在// Read from stream as necessary 部分抛出异常,我认为这不会起作用
            • @sworisbreathing 我已经更新了示例,以便即使在 // Read from stream as necessary 部分出现异常时,也会读取流的其余部分。
            • 我试过你的方法,它显示java.io.IOException: Attempted read on closed stream
            • 这种 try-with-resources 代码样式不起作用,因为资源将在 catchfinally 块执行之前关闭。因此,要使该代码正常工作,您必须切换回旧式 try/catch/finally 块,并在 finally 块中手动关闭。
            【解决方案6】:

            通过其他媒体得到了答案。在这里分享:

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

            这里有两个选项:

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

            【讨论】:

            • “连接可以重复使用”是什么意思?我以为每个 S3 GET 请求都会创建一个 HTTP 连接
            • 嗨 Chirag,您能否详细说明第 1 点(从输入流中读取其余数据,以便可以重用连接)?我的行是这样的: S3ObjectInputStream targetStream = confige.getObjectContent(); XSSFWorkbook excelf = new XSSFWorkbook(targetStream);不知道如何抑制这个警告。
            • @ares SDK 在后台维护了一个连接池,可以为后续请求重用连接以获得更好的性能。
            猜你喜欢
            • 2019-08-14
            • 2020-02-07
            • 2018-06-20
            • 2020-03-01
            • 1970-01-01
            • 1970-01-01
            • 2021-09-19
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多