Загрузить HttpServletRequest в корзину S3 - PullRequest
0 голосов
/ 28 октября 2018

Ниже приведен фрагмент для загрузки inputStream HttpServletRequest в S3 и последующей его загрузки.

    public void init() {
       s3Client = AmazonS3ClientBuilder.defaultClient();    

       //create bucket if not exists
    }

    @PostMapping( Consumes - Multipart/form-data)
    public String send(HttpServletRequest request) {

       //validate request

       boolean isMultipart = ServletFileUpload.isMultipartContent(request);
       if(!isMultipart) // reject

       if (Long.parseLong(request.getHeader("Content-length")) > maxLength) // reject

       //Put request in S3    
       ObjectMetaData objectMetadata = new ObjectMetadata();
       objectMetadata.setContentLength(Long.parseLong(request.getHeader("Content-length")));
       objectMetadata.setContentType(request.getHeader("Content-type"));

       s3Client.putObject(bucket, key, request.getInputStream, objectMetadata);

        CustomServletRequestContext ctx = new CustomServletRequestContext(is, request.getCharacterEncoding(), request.getContentLength(), request.getContentType());

    }

    public void download(CustomServletRequestContext ctx) {
       // Get data from S3
       GetObjectRequest getObjectRequest = new GetObjectRequest(bucket, key);
       S3Object s3Object = s3Client.getObject(getObjectRequest);

       InputStream is = s3Object.getObjectContent();

       ServletFileUpload upload = new ServletFileUpload();
       upload.setSizeMax(maxLength);



       FileItemIterator iterator = upload.getItemIterator(ctx);  // need context object
       while (iterator.hasNext()) {
                FileItemStream item = iterator.next();
                String name = item.getFieldName();
                InputStream stream = item.openStream();
                if (!item.isFormField()) {
                      process IOUtils.toString(stream, "UTF-8") // throws randomly Premature end of Stream exception
                } else {
                      process Streams.asString(stream);
                }

       }
   }

CustomServletRequestContext - необходимо создать его для apache-commons-fileupload api, чтобы получить итератор частей во время чтения, для которого требуется контекст, как написановыше.

class CustomServletRequestContext implements UploadContext {
        private final InputStream is;
        private final String characterEncoding;
        private final int contentLength;
        private final String contentType;


        public MyServletRequestContext(InputStream is, String characterEncoding, int contentLength, String contentType) {
            this.is = is;
            this.characterEncoding = characterEncoding;
            this.contentLength = contentLength;
            this.contentType = contentType;
        }

        public String getCharacterEncoding() {
            return characterEncoding;
        }

        public String getContentType() {
            return contentType;
        }

        public int getContentLength() {
            return contentLength;
        }

        public long contentLength() {

            return contentLength;
        }

        public InputStream getInputStream() throws IOException {
            return is;
        }

        public String toString() {
            return String.format("ContentLength=%s, ContentType=%s", this.contentLength(), this.getContentType());
        }
    }

Обнаружено, что не удается открыть HttpServletRequest inputStream для любого вида проверки, которая требует чтения inputStream перед помещением в S3.В противном случае это приведет к несоответствию длины содержимого позже, так как размеры не будут совпадать, так как inputStream уже был прочитан, а смещение увеличено с 0.

Так как можно выполнить проверку типа содержимого каждой части перед переводом на S3Например, они все изображения?Или все они в простом тексте.это нужно настроить на уровне политики создания корзины?Попытка передачи заголовков ObjectMetadata для "content_type_starts_with" и "content_range" безуспешно.ожидал, что S3 отклонит запрос на размещение, но он прошел успешно.

objectMetadata.setHeader("content_type_starts_with","image/");
objectMetadata.setHeader("content_range","1..100");

Вторая проблема, с которой сталкивается случайное получение преждевременного завершения исключений потока при итерации через поля неформ внутри IoUtils.toString ().Это происходит главным образом во время отладки или форсирования GC между итерациями.Хотя я имею S3Client как переменную класса, не уверен, что вызывает это исключение.Однако, если поток непосредственно считывается в String, а другой InputStream открывается из String, ошибка не возникает.

Преимущества вышеуказанного подхода, временное местоположение не используется, и память также сохраняется при загрузке файлов на S3, так как это операция записи в поток в поток.Также не используется multipart s3 api для загрузки, так как некоторые части имеют ограничение менее 5 МБ.Однако, любым способом мы можем исправить эти 2.

  • Наличие проверок типа проверок типа контента перед загрузкой на S3 или во время запроса на размещение.Я думаю, что политика S3 Bucket может помочь, но некоторые справки / примеры помогут

  • исправить ошибку преждевременного завершения потока при чтении объектов S3 с использованием заметок здесь https://commons.apache.org/proper/commons-fileupload/streaming.html

...