Estoy cargando un archivo en S3 usando Java; esto es lo que obtuve hasta ahora:
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()));
El archivo se está cargando pero aparece una ADVERTENCIA cuando no estoy configurando la longitud del contenido:
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.
Este es un archivo Estoy subiendo y la stream
variable es una InputStream
, de la que puede obtener la matriz de bytes de esta manera: IOUtils.toByteArray(stream)
.
Entonces, cuando trato de establecer la longitud del contenido y MD5 (tomado de aquí ) de esta manera:
// 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);
Provoca que el siguiente error vuelva desde S3:
El Content-MD5 que especificó no es válido.
¿Qué estoy haciendo mal?
¡Cualquier ayuda apreciada!
PD : Estoy en Google App Engine: no puedo escribir el archivo en el disco o crear un archivo temporal porque AppEngine no es compatible con FileOutputStream.
fuente
Si todo lo que está tratando de hacer es resolver el error de longitud del contenido de Amazon, entonces puede leer los bytes del flujo de entrada a Long y agregarlo a los metadatos.
/* * 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(); } }
Deberá leer el flujo de entrada dos veces usando este método exacto, por lo que si está cargando un archivo muy grande, es posible que deba leerlo una vez en una matriz y luego leerlo desde allí.
fuente
Para cargar, el SDK de S3 tiene dos métodos putObject:
y
El método inputstream + ObjectMetadata necesita un mínimo de metadatos de longitud de contenido de su inputstream. Si no lo hace, se almacenará en memoria intermedia para obtener esa información, esto podría causar OOM. Alternativamente, puede hacer su propio almacenamiento en búfer en memoria para obtener la longitud, pero luego necesita obtener un segundo flujo de entrada.
No preguntado por el OP (limitaciones de su entorno), sino por alguien más, como yo. Me resulta más fácil y seguro (si tiene acceso al archivo temporal) escribir el flujo de entrada en un archivo temporal y poner el archivo temporal. Sin búfer en memoria y sin necesidad de crear un segundo flujo de entrada.
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(); } }
fuente
Mientras escribe en S3, debe especificar la longitud del objeto S3 para asegurarse de que no haya errores de memoria insuficiente.
El uso
IOUtils.toByteArray(stream)
también es propenso a errores OOM porque está respaldado por ByteArrayOutputStreamEntonces, la mejor opción es escribir primero el flujo de entrada en un archivo temporal en el disco local y luego usar ese archivo para escribir en S3 especificando la longitud del archivo temporal.
fuente
request.setMetadata();
De hecho, estoy haciendo algo similar pero en mi almacenamiento AWS S3: -
Código para el servlet que está recibiendo el archivo cargado: -
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()); } } }
Código que está cargando estos datos como objeto de 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; } }
Nota: - Estoy usando el archivo de propiedades de AWS para las credenciales.
Espero que esto ayude.
fuente
Creé una biblioteca que usa cargas de varias partes en segundo plano para evitar almacenar todo en la memoria y tampoco escribe en el disco: https://github.com/alexmojaki/s3-stream-upload
fuente
Simplemente pasar el objeto de archivo al método putobject funcionó para mí. Si obtiene una transmisión, intente escribirla en un archivo temporal antes de pasarla a S3.
Estoy usando Aws SDK v1.11.414
La respuesta en https://stackoverflow.com/a/35904801/2373449 me ayudó
fuente
agregar el archivo log4j-1.2.12.jar me ha resuelto el problema
fuente