将HttpServletRequest上传到S3存储桶

时间:2018-10-28 07:41:46

标签: java http amazon-s3 ceph apache-commons-fileupload

下面是将HttpServletRequest的inputStream上传到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创建此文件,以在读取期间获取Parts Iterator,这需要如上所述的Context。

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进行任何需要在放入S3之前需要读取inputStream的验证。否则,这将导致内容长度不匹配,因为由于已经读取inputStream并且偏移量从0增加,因此大小将不匹配。

那么在放入S3之前,如何对每个部分的内容类型进行验证,例如它们都是图像?或者它们都是纯文本。是否需要在创建存储桶策略级别设置此设置?尝试传递“ content_type_starts_with”和“ content_range”的ObjectMetadata标头没有成功。原本希望S3会拒绝看跌请求,但这成功完成了。

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

第二个问题是,在IoUtils.toString()中的非表单字段进行迭代时,随机获取Stream异常的提早结束。这主要发生在调试或强制在两次迭代之间进行GC期间。虽然,我将S3Client作为类变量,但不确定是什么导致了此异常。但是,如果将流直接读入String并从String打开另一个InputStream,则不会出现此错误。

上述方法的优点是,在将文件上传到S3时,不使用临时位置,并且还保存了内存,这是流对流的写操作。另外,由于某些部分小于5MB的限制,因此不使用multipart s3 api进行上传。但是,无论如何,我们都可以修复这些2。

0 个答案:

没有答案