InputStream 길이를 사용하는 AmazonS3 putObject 예시
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()));
파일이 업로드되고 있지만 콘텐츠 길이를 설정하지 않으면 경고 메시지가 나타납니다.
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);
이로 인해 S3에서 다음 오류가 반환됩니다.
지정한 Content-MD5가 잘못되었습니다.
내가 뭘 잘못하고 있지?
아무쪼록 잘 부탁드립니다!
P.S. Google App Engine - AppEngine이 FileOutputStream을 지원하지 않기 때문에 디스크에 파일을 쓰거나 임시 파일을 만들 수 없습니다.
원래 질문에 대한 답변이 없었고, 같은 문제에 직면해야 했기 때문에 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);
기본적으로 MD5 값에 필요한 것은 Hex 문자열이 아니라 Base64로 인코딩된 raw MD5 바이트 배열입니다.이걸로 바꾸니까 잘 되더라고요.
amazon에서 콘텐츠 길이 오류를 해결하려는 경우 입력 스트림에서 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();
}
}
이 방법으로 입력 스트림을 두 번 읽어야 하므로 매우 큰 파일을 업로드할 경우 한 번 어레이로 읽고 거기에서 읽어야 할 수도 있습니다.
업로드를 위해 S3 SDK에는 다음 두 가지 putObject 메서드가 있습니다.
PutObjectRequest(String bucketName, String key, File file)
그리고.
PutObjectRequest(String bucketName, String key, InputStream input, ObjectMetadata metadata)
inputstream+ObjectMetadata 메서드에는 입력 스트림의 Content Length에 대한 최소 메타데이터가 필요합니다.그렇지 않으면 메모리 내 버퍼에 저장하여 정보를 얻습니다.이로 인해 OOM이 발생할 수 있습니다.또는 메모리 내 버퍼링을 실행하여 길이를 취득할 수도 있지만, 그 후 두 번째 입력 스트림을 취득해야 합니다.
작전부(환경의 한계)가 아니라 나 같은 다른 사람을 위해서.입력 스트림을 임시 파일에 쓰고 임시 파일을 저장하는 것이 더 쉽고 안전하다고 생각합니다.메모리 내 버퍼도 없고 두 번째 입력 스트림을 작성할 필요도 없습니다.
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();
}
}
S3에 쓸 때 메모리 부족 오류가 발생하지 않도록 S3 개체의 길이를 지정해야 합니다.
「」를 사용합니다.IOUtils.toByteArray(stream)이 OOM을 하므로 OOM .
따라서 가장 좋은 옵션은 먼저 입력 스트림을 로컬디스크의 임시 파일에 쓴 다음 이 파일을 사용하여 임시 파일의 길이를 지정하여 S3에 쓰는 것입니다.
실제로 AWS S3 스토리지에서도 같은 작업을 하고 있습니다.-
업로드된 파일을 수신하는 서블릿 코드:-
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());
}
}
}
이 데이터를 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;
}
}
참고:- 자격 증명으로 aws 속성 파일을 사용하고 있습니다.
이게 도움이 됐으면 좋겠다.
메모리의 모든 것이 버퍼링되는 것을 피하기 위해 백그라운드에서 멀티파트 업로드를 사용하는 라이브러리를 만들었습니다.또한 디스크에 쓰지 않습니다.https://github.com/alexmojaki/s3-stream-upload
파일 오브젝트를 putobject 메서드로 전달하기만 하면 됩니다.스트림을 얻을 경우 S3에 전달하기 전에 임시 파일에 쓰십시오.
amazonS3.putObject(bucketName, id,fileObject);
AWS SDK v1.11.414를 사용하고 있습니다.
https://stackoverflow.com/a/35904801/2373449의 답변이 도움이 되었습니다.
log4j-1.2.12.jar 파일을 추가하면 문제가 해결되었습니다.
언급URL : https://stackoverflow.com/questions/8351886/amazons3-putobject-with-inputstream-length-example
'programing' 카테고리의 다른 글
| iOS Safari 개인 브라우징 모드에서 Laravel 쿠키가 설정되지 않음 (0) | 2022.09.05 |
|---|---|
| 정수 인덱스로 팬더 시리즈/데이터프레임 행 선택 (0) | 2022.09.05 |
| 테이블 또는 열의 모든 외부 키를 표시하려면 어떻게 해야 합니까? (0) | 2022.09.04 |
| Java에서의 로그업이란? (0) | 2022.09.04 |
| 도커 컨테이너 지연 시간 해결 방법 (0) | 2022.09.04 |