首页 > 解决方案 > 通过 AWS Java SDK 从 S3 读取文本文件时出错

问题描述

我试图通过 JAVA SDK v2 从 AWS S3 读取文本文件,并通过 HTTP(使用 com.sun.net.httpserver.HttpServer)将其发送回客户端。我想将内容作为字符串读取。但是我下面的简单代码不起作用。

问题是什么?如何解决?

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
//...
Region region = Region.US_WEST_2;
String bucketName = "file-store";

S3Client s3Client = S3Client.builder().region(region).build();
//...
class GetFileHandlerV2 implements HttpHandler {
@Override
public void handle(HttpExchange he) throws IOException {
GetObjectRequest getObjectRequest = GetObjectRequest.builder()
                                         .bucket(bucketName)
                                         .key(id + "/files/" + id + ".txt")
                                         .build();
OutputStream os = he.getResponseBody();
s3Client.getObject(getObjectRequest, ResponseTransformer.toOutputStream(os));
os.close();
//...
}
}

以下是错误:

java.io.IOException: response headers not sent yet
        at sun.net.httpserver.PlaceholderOutputStream.checkWrap(ExchangeImpl.java:433) ~[jdk.httpserver:?]
        at sun.net.httpserver.PlaceholderOutputStream.write(ExchangeImpl.java:448) ~[jdk.httpserver:?]
        at java.io.InputStream.transferTo(InputStream.java:772) ~[?:?]
        at comcast.labs.objectstore.FileRetriever$GetFileHandlerV2.handle(FileRetriever.java:112) [FileRetriever-1.0.jar:?]
        at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:77) [jdk.httpserver:?]
        at sun.net.httpserver.AuthFilter.doFilter(AuthFilter.java:82) [jdk.httpserver:?]
        at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:80) [jdk.httpserver:?]
        at sun.net.httpserver.ServerImpl$Exchange$LinkHandler.handle(ServerImpl.java:692) [jdk.httpserver:?]
        at com.sun.net.httpserver.Filter$Chain.doFilter(Filter.java:77) [jdk.httpserver:?]
        at sun.net.httpserver.ServerImpl$Exchange.run(ServerImpl.java:664) [jdk.httpserver:?]
        at sun.net.httpserver.ServerImpl$DefaultExecutor.execute(ServerImpl.java:159) [jdk.httpserver:?]
        at sun.net.httpserver.ServerImpl$Dispatcher.handle(ServerImpl.java:442) [jdk.httpserver:?]
        at sun.net.httpserver.ServerImpl$Dispatcher.run(ServerImpl.java:408) [jdk.httpserver:?]
        at java.lang.Thread.run(Thread.java:835) [?:?]

标签: javaamazon-web-servicesamazon-s3aws-java-sdk-2.xcom.sun.net.httpserver

解决方案


请考虑查看HttpExchange 文档。它提供了HttpExchange.

具体来说,它表示在写入响应正文之前,必须调用OutputStream返回getResponseBody的方法才能真正开始向客户端发送信息。sendResponseHeadersjavadoc

开始使用当前的一组响应标头和此方法中指定的数字响应代码将响应发送回客户端...。

请按照您的示例尝试以下操作:

@Override
public void handle(HttpExchange he) throws IOException {
  GetObjectRequest getObjectRequest = GetObjectRequest.builder()
                                         .bucket(bucketName)
                                         .key(id + "/files/" + id + ".txt")
                                         .build();

  // Optional, and according to your file extension
  he.getResponseHeaders().set("Content-type", "text/plain");

  // Set the `responseLength` to zero, from the docs: chunked transfer encoding
  // will be used and an arbitrary amount of data may be sent.
  he.sendResponseHeaders(200, 0);

  // The rest of your code
  OutputStream os = he.getResponseBody();
  s3Client.getObject(getObjectRequest, ResponseTransformer.toOutputStream(os));
  os.close();
  //...
}

您可以阅读S3Clientfirst 返回的全部信息,然后将其包装在您的处理程序上:

@Override
public void handle(HttpExchange he) throws IOException {
  // Fist, download actual object from S3 in the way you consider appropriate
  // This fragment of code could be refactored and be defined in its own
  // class/method
  GetObjectRequest getObjectRequest = GetObjectRequest.builder()
                                         .bucket(bucketName)
                                         .key(id + "/files/" + id + ".txt")
                                         .build();
  ByteArrayOutputStream bos = new ByteArrayOutputStream();
  s3Client.getObject(getObjectRequest, ResponseTransformer.toOutputStream(bos));
  
  byte[] bytes = bos.toByteArray();

  // Perform the actual exchange

  // Optional, and according to your file extension
  he.getResponseHeaders().set("Content-type", "text/plain");

  he.sendResponseHeaders(200, bytes.length);
  // Please, perform the optimizations (buffering, etcetera) that you
  // consider necessary when writing the information to the user
  he.getResponseBody().write(bytes);
  he.close();
}

第二种方法将允许您在需要时重构代码,将与 S3 交互相关的逻辑与与 HTTP 包装代码相关的逻辑区分开来。


推荐阅读