AmazonS3 putObject with inputstream length example
Wgrywam plik Na S3 za pomocą Javy - to mam do tej pory:
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()));
Plik jest przesyłany, ale pojawia się ostrzeżenie, gdy nie ustawiam długości zawartości:
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.
Jest to plik, który przesyłam, a zmienną stream
jest InputStream
, z którego mogę uzyskać tablicę bajtów w następujący sposób: IOUtils.toByteArray(stream)
.
Więc kiedy próbuję ustawić długość zawartości I MD5 (wzięte z tutaj) Tak:
// 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);
Powoduje następujący błąd wróć z S3:
Co robię źle?Podany Content-MD5 był nieprawidłowy.
Każda pomoc doceniona!
P. S. jestem na Google App Engine - nie mogę zapisać pliku na dysk lub utworzyć plik tymczasowy ponieważ AppEngine nie obsługuje FileOutputStream.
7 answers
Ponieważ oryginalne pytanie nigdy nie zostało udzielone, a ja musiałem napotkać ten sam problem, rozwiązaniem problemu MD5 jest to, że S3 nie chce zakodowanego szesnastkowo ciągu MD5, o którym zwykle myślimy.
Zamiast tego musiałem to zrobić.// content is a passed in InputStream
byte[] resultByte = DigestUtils.md5(content);
String streamMD5 = new String(Base64.encodeBase64(resultByte));
metaData.setContentMD5(streamMD5);
Zasadniczo to, czego chcą dla wartości MD5, to zakodowana w Base64 surowa tablica bajtów MD5, a nie Łańcuch szesnastkowy. Kiedy przełączyłem się na to, zaczęło działać świetnie dla mnie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-26 20:31:41
Jeśli wszystko, co próbujesz zrobić, to rozwiązać błąd długości zawartości z amazon, możesz po prostu odczytać bajty ze strumienia wejściowego do długiego i dodać to do metadanych.
/*
* 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();
}
}
Będziesz musiał odczytać strumień wejściowy dwa razy używając dokładnie tej metody, więc jeśli przesyłasz bardzo duży plik, możesz potrzebować spojrzeć na odczytanie go raz do tablicy, a następnie odczytanie go stamtąd.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-19 07:06:47
Do przesyłania, S3 SDK ma dwie metody putObject:
PutObjectRequest(String bucketName, String key, File file)
I
PutObjectRequest(String bucketName, String key, InputStream input, ObjectMetadata metadata)
Metoda inputstream + ObjectMetadata wymaga minimalnej długości metadanych zawartości strumienia wejściowego. Jeśli nie, to będzie buforować w pamięci, aby uzyskać te informacje, może to spowodować OOM. Alternatywnie, możesz zrobić własne buforowanie w pamięci, aby uzyskać długość, ale musisz uzyskać drugi strumień wejściowy.
Nie pytany przez OP (ograniczenia jego środowiska), ale dla kogoś inne, takie jak ja. Uważam, że łatwiej i bezpieczniej (jeśli masz dostęp do pliku tymczasowego), zapisać strumień wejściowy do pliku tymczasowego i umieścić plik tymczasowy. Brak bufora w pamięci i brak wymogu utworzenia drugiego strumienia wejściowego.
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();
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-19 07:07:08
Podczas zapisu do S3, musisz określić długość obiektu S3, aby mieć pewność, że nie ma błędów z pamięci.
Użycie IOUtils.toByteArray(stream)
jest również podatne na błędy OOM, ponieważ jest to wspierane przez ByteArrayOutputStream
Więc najlepszą opcją jest najpierw zapisanie strumienia wejściowego do pliku tymczasowego na dysku lokalnym, a następnie użycie tego pliku do zapisu do S3 poprzez podanie długości pliku tymczasowego.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-02 05:28:17
Faktycznie robię to samo, ale na moim AWS S3 storage:-
Kod dla servletu, który otrzymuje przesłany plik:-
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());
}
}
}
Kod, który wgrywa te dane jako obiekt 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;
}
}
Uwaga: - używam pliku właściwości aws dla poświadczeń.
Mam nadzieję, że to pomoże.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-21 10:19:22
Stworzyłem bibliotekę, która używa plików wieloczęściowych w tle, aby uniknąć buforowania wszystkiego w pamięci, a także nie zapisuje na dysk: https://github.com/alexmojaki/s3-stream-upload
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-22 14:11:43
Dodanie log4j-1.2.12.plik jar rozwiązał problem
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-27 20:57:22