Wgrywam plik do S3 za pomocą Javy - tak 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 treś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.
Ten plik jest mi przesłać, a stream
zmienna jest InputStream
, z którego można uzyskać tablicę bajtów takiego: IOUtils.toByteArray(stream)
.
Więc kiedy próbuję ustawić długość treści i MD5 (wzięte stąd ) w ten sposób:
// 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 to powrót następującego błędu z S3:
Podany Content-MD5 był nieprawidłowy.
Co ja robię źle?
Każda pomoc doceniona!
PS Korzystam z Google App Engine - nie mogę zapisać pliku na dysk ani utworzyć pliku tymczasowego, ponieważ AppEngine nie obsługuje funkcji FileOutputStream.
java
google-app-engine
amazon-s3
md5
inputstream
JohnIdol
źródło
źródło
Odpowiedzi:
Ponieważ na pierwotne pytanie nigdy nie udzielono odpowiedzi i musiałem napotkać ten sam problem, rozwiązaniem problemu MD5 jest to, że S3 nie chce łańcucha MD5 zakodowanego szesnastkowo, o którym normalnie 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 tablica bajtów MD5 zakodowana algorytmem Base64, a nie ciąg szesnastkowy. Kiedy przeszedłem na to, zaczęło mi działać świetnie.
źródło
Jeśli wszystko, co próbujesz zrobić, to rozwiązać błąd długości treści z amazon, możesz po prostu odczytać bajty ze strumienia wejściowego do Long 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ł dwukrotnie odczytać strumień wejściowy, używając tej dokładnej metody, więc jeśli przesyłasz bardzo duży plik, być może będziesz musiał spojrzeć na wczytanie go raz do tablicy, a następnie odczytanie stamtąd.
źródło
Do przesyłania S3 SDK ma dwie metody putObject:
i
Metoda inputstream + ObjectMetadata wymaga minimalnych metadanych długości zawartości strumienia wejściowego. Jeśli tego nie zrobisz, buforuje w pamięci, aby uzyskać te informacje, co może spowodować OOM. Alternatywnie możesz zrobić własne buforowanie w pamięci, aby uzyskać długość, ale wtedy musisz uzyskać drugi strumień wejściowy.
Nie pytany przez OP (ograniczenia jego środowiska), ale dla kogoś innego, takiego jak ja. Uważam, że łatwiej i bezpieczniej (jeśli masz dostęp do pliku tymczasowego), aby zapisać strumień wejściowy do pliku tymczasowego i umieścić plik tymczasowy. Brak bufora w pamięci i brak konieczności tworzenia 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(); } }
źródło
Pisząc do S3, musisz określić długość obiektu S3, aby mieć pewność, że nie ma błędów braku pamięci.
Używanie
IOUtils.toByteArray(stream)
jest również podatne na błędy OOM, ponieważ jest obsługiwane przez ByteArrayOutputStreamDlatego najlepszą opcją jest najpierw zapisanie strumienia wejściowego do pliku tymczasowego na dysku lokalnym, a następnie użycie tego pliku do zapisu w S3, określając długość pliku tymczasowego.
źródło
request.setMetadata();
faktycznie robię to samo, ale na moim magazynie AWS S3: -
Kod serwletu odbierającego wgrany 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 przesyła 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 do poświadczeń.
Mam nadzieję że to pomoże.
źródło
Utworzyłem bibliotekę, która używa przesyłania wieloczęściowego w tle, aby uniknąć buforowania wszystkiego w pamięci, a także nie zapisuje na dysku: https://github.com/alexmojaki/s3-stream-upload
źródło
Po prostu przekazanie obiektu pliku do metody putobject działało dla mnie. Jeśli otrzymujesz strumień, spróbuj zapisać go w pliku tymczasowym przed przekazaniem go do S3.
Używam Aws SDK w wersji 1.11.414
Pomogła mi odpowiedź na https://stackoverflow.com/a/35904801/2373449
źródło
dodanie pliku log4j-1.2.12.jar rozwiązało ten problem
źródło