Joh*_*dol 75 java google-app-engine md5 inputstream amazon-s3
我正在使用Java将文件上传到S3 - 这是我到目前为止所得到的:
AmazonS3 s3 = new AmazonS3Client(new BasicAWSCredentials("XX","YY"));
List<Bucket> buckets = s3.listBuckets();
s3.putObject(new PutObjectRequest(buckets.get(0).getName(), fileName, stream, new ObjectMetadata()));
Run Code Online (Sandbox Code Playgroud)
正在上传文件,但在我未设置内容长度时会引发警告:
Run Code Online (Sandbox Code Playgroud)com.amazonaws.services.s3.AmazonS3Client putObject: No content length specified for stream > data. Stream contents will be buffered in memory and could result in out of memory errors.
这是我上传的文件,stream变量是一个InputStream,我可以从中得到这样的字节数组:IOUtils.toByteArray(stream).
所以,当我尝试设置内容长度和MD5(从这里取得)像这样:
// get MD5 base64 hash
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.reset();
messageDigest.update(IOUtils.toByteArray(stream));
byte[] resultByte = messageDigest.digest();
String hashtext = new String(Hex.encodeHex(resultByte));
ObjectMetadata meta = new ObjectMetadata();
meta.setContentLength(IOUtils.toByteArray(stream).length);
meta.setContentMD5(hashtext);
Run Code Online (Sandbox Code Playgroud)
它会导致以下错误从S3返回:
您指定的Content-MD5无效.
我究竟做错了什么?
任何帮助赞赏!
PS我在Google App Engine上 - 我无法将文件写入磁盘或创建临时文件,因为AppEngine不支持FileOutputStream.
小智 67
因为原始问题从未得到解答,我不得不遇到同样的问题,MD5问题的解决方案是S3不想要我们通常想到的Hex编码的MD5字符串.
相反,我必须这样做.
// content is a passed in InputStream
byte[] resultByte = DigestUtils.md5(content);
String streamMD5 = new String(Base64.encodeBase64(resultByte));
metaData.setContentMD5(streamMD5);
Run Code Online (Sandbox Code Playgroud)
基本上他们想要的MD5值是Base64编码的原始MD5字节数组,而不是Hex字符串.当我切换到这个时,它开始为我工作.
tar*_*rka 42
如果您要做的就是解决亚马逊的内容长度错误,那么您只需将输入流中的字节读取到Long并将其添加到元数据中.
/*
* Obtain the Content length of the Input stream for S3 header
*/
try {
InputStream is = event.getFile().getInputstream();
contentBytes = IOUtils.toByteArray(is);
} catch (IOException e) {
System.err.printf("Failed while reading bytes from %s", e.getMessage());
}
Long contentLength = Long.valueOf(contentBytes.length);
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(contentLength);
/*
* Reobtain the tmp uploaded file as input stream
*/
InputStream inputStream = event.getFile().getInputstream();
/*
* Put the object in S3
*/
try {
s3client.putObject(new PutObjectRequest(bucketName, keyName, inputStream, metadata));
} catch (AmazonServiceException ase) {
System.out.println("Error Message: " + ase.getMessage());
System.out.println("HTTP Status Code: " + ase.getStatusCode());
System.out.println("AWS Error Code: " + ase.getErrorCode());
System.out.println("Error Type: " + ase.getErrorType());
System.out.println("Request ID: " + ase.getRequestId());
} catch (AmazonClientException ace) {
System.out.println("Error Message: " + ace.getMessage());
} finally {
if (inputStream != null) {
inputStream.close();
}
}
Run Code Online (Sandbox Code Playgroud)
您需要使用这种精确的方法读取输入流两次,因此如果您要上传一个非常大的文件,您可能需要先将其读入数组,然后从那里读取它.
Pet*_*etz 28
对于上传,S3 SDK有两个putObject方法:
PutObjectRequest(String bucketName, String key, File file)
Run Code Online (Sandbox Code Playgroud)
和
PutObjectRequest(String bucketName, String key, InputStream input, ObjectMetadata metadata)
Run Code Online (Sandbox Code Playgroud)
inputstream + ObjectMetadata方法需要输入流的Content Length的最小元数据.如果不这样做,那么它将在内存中缓冲以获取该信息,这可能会导致OOM.或者,您可以使用自己的内存缓冲来获取长度,但是您需要获得第二个输入流.
OP没有提出要求(他的环境有限),但是对于其他人,比如我.我发现它更容易,更安全(如果你有权访问临时文件),将输入流写入临时文件,并放入临时文件.没有内存缓冲区,也没有要求创建第二个输入流.
AmazonS3 s3Service = new AmazonS3Client(awsCredentials);
File scratchFile = File.createTempFile("prefix", "suffix");
try {
FileUtils.copyInputStreamToFile(inputStream, scratchFile);
PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, id, scratchFile);
PutObjectResult putObjectResult = s3Service.putObject(putObjectRequest);
} finally {
if(scratchFile.exists()) {
scratchFile.delete();
}
}
Run Code Online (Sandbox Code Playgroud)
写入S3时,需要指定S3对象的长度,以确保没有内存不足错误.
使用IOUtils.toByteArray(stream)也容易出现OOM错误,因为这是由ByteArrayOutputStream支持的
因此,最好的选择是首先将输入流写入本地磁盘上的临时文件,然后通过指定临时文件的长度使用该文件写入S3.
我实际上在做一些相同的事情,但是在我的AWS S3存储上:-
正在接收上传文件的servlet的代码:-
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import com.src.code.s3.S3FileUploader;
public class FileUploadHandler extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
PrintWriter out = response.getWriter();
try{
List<FileItem> multipartfiledata = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
//upload to S3
S3FileUploader s3 = new S3FileUploader();
String result = s3.fileUploader(multipartfiledata);
out.print(result);
} catch(Exception e){
System.out.println(e.getMessage());
}
}
}
Run Code Online (Sandbox Code Playgroud)
将数据作为AWS对象上传的代码:-
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.List;
import java.util.UUID;
import org.apache.commons.fileupload.FileItem;
import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.auth.ClasspathPropertiesFileCredentialsProvider;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.S3Object;
public class S3FileUploader {
private static String bucketName = "***NAME OF YOUR BUCKET***";
private static String keyName = "Object-"+UUID.randomUUID();
public String fileUploader(List<FileItem> fileData) throws IOException {
AmazonS3 s3 = new AmazonS3Client(new ClasspathPropertiesFileCredentialsProvider());
String result = "Upload unsuccessfull because ";
try {
S3Object s3Object = new S3Object();
ObjectMetadata omd = new ObjectMetadata();
omd.setContentType(fileData.get(0).getContentType());
omd.setContentLength(fileData.get(0).getSize());
omd.setHeader("filename", fileData.get(0).getName());
ByteArrayInputStream bis = new ByteArrayInputStream(fileData.get(0).get());
s3Object.setObjectContent(bis);
s3.putObject(new PutObjectRequest(bucketName, keyName, bis, omd));
s3Object.close();
result = "Uploaded Successfully.";
} catch (AmazonServiceException ase) {
System.out.println("Caught an AmazonServiceException, which means your request made it to Amazon S3, but was "
+ "rejected with an error response for some reason.");
System.out.println("Error Message: " + ase.getMessage());
System.out.println("HTTP Status Code: " + ase.getStatusCode());
System.out.println("AWS Error Code: " + ase.getErrorCode());
System.out.println("Error Type: " + ase.getErrorType());
System.out.println("Request ID: " + ase.getRequestId());
result = result + ase.getMessage();
} catch (AmazonClientException ace) {
System.out.println("Caught an AmazonClientException, which means the client encountered an internal error while "
+ "trying to communicate with S3, such as not being able to access the network.");
result = result + ace.getMessage();
}catch (Exception e) {
result = result + e.getMessage();
}
return result;
}
}
Run Code Online (Sandbox Code Playgroud)
注意:-我正在使用aws属性文件获取凭据。
希望这可以帮助。