Я загружаю файл на S3 с помощью Java - вот что у меня получилось:
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.
Что я делаю неправильно?
Любая помощь приветствуется!
PS Я использую Google App Engine - я не могу записать файл на диск или создать временный файл, потому что AppEngine не поддерживает FileOutputStream.
java
google-app-engine
amazon-s3
md5
inputstream
JohnIdol
источник
источник
Ответы:
Поскольку на исходный вопрос так и не ответили, и мне пришлось столкнуться с той же проблемой, решение проблемы MD5 состоит в том, что S3 не хочет строку MD5 в шестнадцатеричном коде, о которой мы обычно думаем.
Вместо этого мне пришлось сделать это.
// content is a passed in InputStream byte[] resultByte = DigestUtils.md5(content); String streamMD5 = new String(Base64.encodeBase64(resultByte)); metaData.setContentMD5(streamMD5);
По сути, то, что они хотят для значения MD5, - это необработанный массив байтов MD5 в кодировке Base64, а не строка Hex. Когда я перешел на это, у меня все стало отлично работать.
источник
Если все, что вы пытаетесь сделать, это решить ошибку длины контента от 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:
и
Для метода inputstream + ObjectMetadata требуются минимальные метаданные Content Length вашего inputstream. Если вы этого не сделаете, он будет буферизовать в памяти для получения этой информации, это может вызвать OOM. В качестве альтернативы вы можете выполнить собственную буферизацию в памяти, чтобы получить длину, но тогда вам нужно получить второй входной поток.
Не спрашивает OP (ограничения его среды), но для кого-то другого, например, меня. Я считаю, что проще и безопаснее (если у вас есть доступ к временному файлу) записать входной поток во временный файл и поместить временный файл. Нет буфера в памяти и нет необходимости создавать второй поток ввода.
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, поскольку это поддерживается ByteArrayOutputStreamИтак, лучший вариант - сначала записать входной поток во временный файл на локальном диске, а затем использовать этот файл для записи в S3, указав длину временного файла.
источник
request.setMetadata();
На самом деле я делаю то же самое, но на своем хранилище 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.
Я использую Aws SDK v1.11.414
Ответ на https://stackoverflow.com/a/35904801/2373449 мне помог
источник
добавление файла log4j-1.2.12.jar решило проблему для меня
источник