Ich lade eine Datei mit Java auf S3 hoch - das habe ich bisher:
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()));
Die Datei wird hochgeladen, aber eine WARNUNG wird ausgelöst, wenn ich die Inhaltslänge nicht einstelle:
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.
Dies ist eine Datei, die ich hochlade, und die stream
Variable ist eine InputStream
, aus der ich das Byte-Array wie folgt abrufen kann : IOUtils.toByteArray(stream)
.
Wenn ich also versuche, die Inhaltslänge und MD5 (von hier übernommen ) wie folgt einzustellen :
// 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);
Der folgende Fehler wird von S3 zurückgegeben:
Das von Ihnen angegebene Content-MD5 war ungültig.
Was mache ich falsch?
Jede Hilfe geschätzt!
PS Ich bin in Google App Engine - Ich kann die Datei nicht auf die Festplatte schreiben oder eine temporäre Datei erstellen, da AppEngine FileOutputStream nicht unterstützt.
quelle
Wenn Sie nur versuchen, den Fehler bei der Inhaltslänge von Amazon zu beheben, können Sie einfach die Bytes aus dem Eingabestream zu einem Long lesen und diese zu den Metadaten hinzufügen.
/* * 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(); } }
Sie müssen den Eingabestream zweimal genau mit dieser Methode lesen. Wenn Sie also eine sehr große Datei hochladen, müssen Sie sie möglicherweise einmal in ein Array einlesen und dann von dort aus lesen.
quelle
Zum Hochladen verfügt das S3 SDK über zwei putObject-Methoden:
und
Die Methode inputstream + ObjectMetadata benötigt eine Mindestmetadate der Inhaltslänge Ihres Eingabestreams. Wenn Sie dies nicht tun, wird der Speicher im Speicher gepuffert, um diese Informationen abzurufen. Dies kann zu OOM führen. Alternativ können Sie Ihre eigene In-Memory-Pufferung durchführen, um die Länge zu ermitteln. Dann müssen Sie jedoch einen zweiten Eingabestream abrufen.
Nicht vom OP gefragt (Einschränkungen seiner Umgebung), sondern für jemand anderen, wie mich. Ich finde es einfacher und sicherer (wenn Sie Zugriff auf temporäre Dateien haben), den Eingabestream in eine temporäre Datei zu schreiben und die temporäre Datei abzulegen. Kein speicherinterner Puffer und keine Notwendigkeit, einen zweiten Eingabestream zu erstellen.
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(); } }
quelle
Beim Schreiben in S3 müssen Sie die Länge des S3-Objekts angeben, um sicherzustellen, dass keine Speicherfehler vorliegen.
Die Verwendung
IOUtils.toByteArray(stream)
ist auch anfällig für OOM-Fehler, da dies von ByteArrayOutputStream unterstützt wirdDie beste Option besteht also darin, zuerst den Eingabestream in eine temporäre Datei auf der lokalen Festplatte zu schreiben und dann mit dieser Datei in S3 zu schreiben, indem die Länge der temporären Datei angegeben wird.
quelle
request.setMetadata();
Ich mache eigentlich etwas das Gleiche, aber auf meinem AWS S3-Speicher: -
Code für das Servlet, das die hochgeladene Datei empfängt: -
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()); } } }
Code, der diese Daten als AWS-Objekt hochlädt: -
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; } }
Hinweis: - Ich verwende die aws-Eigenschaftendatei für Anmeldeinformationen.
Hoffe das hilft.
quelle
Ich habe eine Bibliothek erstellt, die mehrteilige Uploads im Hintergrund verwendet, um zu vermeiden, dass alles im Speicher gepuffert wird, und auch nicht auf die Festplatte schreibt: https://github.com/alexmojaki/s3-stream-upload
quelle
Das Übergeben des Dateiobjekts an die putobject-Methode hat bei mir funktioniert. Wenn Sie einen Stream erhalten, versuchen Sie, ihn in eine temporäre Datei zu schreiben, bevor Sie ihn an S3 weitergeben.
Ich verwende Aws SDK v1.11.414
Die Antwort unter https://stackoverflow.com/a/35904801/2373449 hat mir geholfen
quelle
Das Hinzufügen der Datei log4j-1.2.12.jar hat das Problem für mich behoben
quelle