使用Java从S3存储桶读取文件,并以模拟实际文件上载的方式将HTTP PUT文件发送到另一个存储桶的预先签名的AWS S3 URL
Java和HTTP请求的新特性 为什么这个问题不是重复的:我没有使用AWS SDK生成任何预先签名的URL。我从外部API获取它 以下是我试图实现的目标: 步骤1:读取文件的源S3存储桶(目前为使用Java从S3存储桶读取文件,并以模拟实际文件上载的方式将HTTP PUT文件发送到另一个存储桶的预先签名的AWS S3 URL,java,amazon-web-services,file,file-io,http-headers,Java,Amazon Web Services,File,File Io,Http Headers,Java和HTTP请求的新特性 为什么这个问题不是重复的:我没有使用AWS SDK生成任何预先签名的URL。我从外部API获取它 以下是我试图实现的目标: 步骤1:读取文件的源S3存储桶(目前为.xlsx) 步骤2:通过将该文件转换为InputStreamReader来解析该文件(我需要这里的帮助) 步骤3:通过将InputStreamReader的内容传输到OutputStreamWriter,在我已经从外部团队获得的预签名S3URL上,对该文件执行HTTP PUT。文件必须位于目标S3存储桶
.xlsx
)
步骤2:通过将该文件转换为InputStreamReader来解析该文件(我需要这里的帮助)
步骤3:通过将InputStreamReader的内容传输到OutputStreamWriter,在我已经从外部团队获得的预签名S3URL上,对该文件执行HTTP PUT。文件必须位于目标S3存储桶中,就像通过拖放手动上载文件一样。(这里也需要帮助)
以下是我尝试过的:
步骤1:读取文件的S3存储桶
public class LambdaMain implements RequestHandler<S3Event, String> {
@Override
public String handleRequest(final S3Event event, final Context context) {
System.out.println("Create object was called on the S3 bucket");
S3EventNotification.S3EventNotificationRecord record = event.getRecords().get(0);
String srcBucket = record.getS3().getBucket().getName();
String srcKey = record.getS3().getObject().getUrlDecodedKey();
AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
.withCredentials(DefaultAWSCredentialsProviderChain.getInstance())
.build();
S3Object s3Object = s3Client.getObject(new GetObjectRequest(
srcBucket, srcKey));
String presignedS3Url = //Assume that I have this by making an external API call
InputStreamReader inputStreamReader = parseFileFromS3(s3Object); #Step 2
int responseCode = putContentIntoS3URL(inputStreamReader, presignedS3Url); #Step 3
}
步骤3:通过将内容从InputStreamReader
复制到OutputStreamWriter
:
private InputStreamReader parseFileFromS3(S3Object s3Object) {
return new InputStreamReader(s3Object.getObjectContent(), StandardCharsets.UTF_8);
}
private int putContentIntoS3URL(InputStreamReader inputStreamReader, String presignedS3Url) {
URL url = null;
try {
url = new URL(presignedS3Url);
} catch (MalformedURLException e) {
e.printStackTrace();
}
HttpURLConnection httpCon = null;
try {
assert url != null;
httpCon = (HttpURLConnection) url.openConnection();
} catch (IOException e) {
e.printStackTrace();
}
httpCon.setDoOutput(true);
try {
httpCon.setRequestMethod("PUT");
} catch (ProtocolException e) {
e.printStackTrace();
}
OutputStreamWriter outputStreamWriter = null;
try {
outputStreamWriter = new OutputStreamWriter(
httpCon.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
try {
IOUtils.copy(inputStreamReader, outputStreamWriter);
} catch (IOException e) {
e.printStackTrace();
}
try {
outputStreamWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
httpCon.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
int responseCode = 0;
try {
responseCode = httpCon.getResponseCode();
} catch (IOException e) {
e.printStackTrace();
}
return responseCode;
}
中间方法的问题是,当我通过S3 insert触发器读取.xlsx
文件并将其放入URL时,当我下载上传的文件时,它会作为一些胡言乱语被下载
当我尝试读取.png
文件并将其放入URL时,当我下载上传的文件时,它会被下载为带有一些胡言乱语的文本文件(不过我确实在其中看到了png这个词)
感觉我在以下方面犯了错误:
OutputStreamWriter
,因为我不知道如何通过HTTP请求发送文件内容类型
setRequestProperty(“Content Type”,“application/vnd.openxmlformats officedocument.spreadsheetml.sheet”)
,但该文件不包含在具有文件扩展名的S3存储桶中。它只是作为一个对象放在那里
更新2:
我认为这也与setContentDisposition()
头有关,尽管我不确定如何为Excel文件设置这些头
更新3:
这可能仅仅与预先签名的S3URL本身是如何出售给我们有关。正如问题中提到的,我说我们从其他团队获得了预先签名的S3URL。问题本身有多个部分需要回答
内容类型
和内容处置
:我在这里设置了另一个单独的问题,因为它非常不清楚:OutputStream
您正在使用InputStreamReader和OutputStreamWriter,它们都是字节流和字符流之间的桥梁。但是,您将这些用于字节数据,这意味着您首先将字节转换为字符,然后再转换回字节。因为您的数据不是字符数据,所以这种转换可能解释了为什么您会因此变得胡言乱语 我开始尝试摆脱读写器,而是直接使用InputStream(您已经从s3Object.getObjectContent()获得)和OutputStream(您从httpCon.getOutputStream()获得)。IOUtils.copy也应该支持这一点
另外,在构造InputStreamReader时,您将StandardCharsets.UTF_8设置为要使用的字符集,但在构造OutputStreamWriter时,您不会设置字符集。如果默认字符集不是UTF-8,此转换可能还会导致乱码。为什么不使用AWS SDK for Java而不是较低级别的HTTP方法?请看,其他团队给我们预先指定的S3URL是一个很难满足的要求。因为它涉及很多安全认证,所以我们自己不这么做,而是让其他团队来处理预先签名的S3URL创建。有趣。看起来AWS文档中也有一些关于这方面的指导。(显然支持设置内容类型)。您可以尝试编写代码来上传到一个简单的web服务,该服务接受通过HTTP上传文件。一旦你开始工作,修改你的代码来使用S3。