接受Multipart文件上传为camel restlet或cxfrs端点

时间:2014-11-10 16:34:15

标签: apache-camel restlet

我希望实现一个路由,其中​​reslet / cxfrs端点将接受文件作为多部分请求和进程。 (请求也可能包含一些JSON数据。

提前致谢。 问候。 [编辑] 试过以下代码。还尝试使用curl发送文件。我可以在标题和调试输出中看到文件相关信息,但无法检索附件。

from("servlet:///hello").process(new Processor() {
   @Override
   public void process(Exchange exchange) throws Exception {
      Message in = exchange.getIn();
      StringBuffer v = new StringBuffer();
       HttpServletRequest request = (HttpServletRequest) in
          .getHeaders().get(Exchange.HTTP_SERVLET_REQUEST);

       DiskFileItemFactory diskFile = new DiskFileItemFactory();
       FileItemFactory factory = diskFile;
       ServletFileUpload upload = new ServletFileUpload(factory);
       List items = upload.parseRequest(request);
..... 
卷曲: curl -vvv -i -X POST -H"内容类型:multipart / form-data" -F" image = @ / Users / navaltiger / 1.jpg;类型=图像/ JPG" HTTP://:8080 / JettySample /骆驼/你好

以下代码有效(但是在嵌入jetty时无法使用,我们希望将其部署在tomcat / weblogic上)

public void configure() throws Exception {
        // getContext().getProperties().put("CamelJettyTempDir", "target");
        getContext().setStreamCaching(true);
        getContext().setTracing(true);

         from("jetty:///test").process(new Processor() {
//      from("servlet:///hello").process(new Processor() {
            public void process(Exchange exchange) throws Exception {

                String body = exchange.getIn().getBody(String.class);
                HttpServletRequest request = exchange.getIn().getBody(
                        HttpServletRequest.class);

                StringBuffer v = new StringBuffer();
                // byte[] picture = (request.getParameter("image")).getBytes();

                v.append("\n Printing All Request Parameters From HttpSerlvetRequest: \n+"+body +" \n\n");

                Enumeration<String> requestParameters = request
                        .getParameterNames();
                while (requestParameters.hasMoreElements()) {
                    String paramName = (String) requestParameters.nextElement();
                    v.append("\n Request Paramter Name: " + paramName
                            + ", Value - " + request.getParameter(paramName));
                }

4 个答案:

答案 0 :(得分:3)

我有一个类似的问题,并设法解决brentos的答案的灵感。我的案例中的其余端点是通过xml:

定义的
<restContext id="UploaderServices"  xmlns="http://camel.apache.org/schema/spring">

    <rest path="/uploader">
        <post bindingMode="off" uri="/upload"  produces="application/json">
            <to uri="bean:UploaderService?method=uploadData"/>
        </post>
    </rest>

</restContext>

我不得不使用“bindingMode = off”来禁用xml / json解组,因为HttpRequest主体包含多部分数据(json / text + file),显然标准的解组过程无法处理请求,因为它需要一个字符串身体而不是多部分有效载荷。

文件和其他参数是从使用文件上传角度模块的前端发送的:https://github.com/danialfarid/ng-file-upload

要解决CORS问题,我必须在web.xml中添加一个CORSFilter过滤器,如下所示:

public class CORSFilter implements Filter {

@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException,
        ServletException {
    HttpServletResponse httpResp = (HttpServletResponse) resp;
    HttpServletRequest httpReq = (HttpServletRequest) req;

    httpResp.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH");
    httpResp.setHeader("Access-Control-Allow-Origin", "*");
    if (httpReq.getMethod().equalsIgnoreCase("OPTIONS")) {
        httpResp.setHeader("Access-Control-Allow-Headers",
                httpReq.getHeader("Access-Control-Request-Headers"));
    }
    chain.doFilter(req, resp);
}

@Override
public void init(FilterConfig arg0) throws ServletException {
}

@Override
public void destroy() {
}
}

另外,我不得不修改一下解组部分:

public String uploadData(Message exchange) {
    String contentType=(String) exchange.getIn().getHeader(Exchange.CONTENT_TYPE);
    MediaType mediaType = MediaType.valueOf(contentType); //otherwise the boundary parameter is lost
    InputRepresentation representation = new InputRepresentation(exchange
            .getBody(InputStream.class), mediaType);

    try {
        List<FileItem> items = new RestletFileUpload(
                new DiskFileItemFactory())
                .parseRepresentation(representation);

        for (FileItem item : items) {
            if (!item.isFormField()) {
                InputStream inputStream = item.getInputStream();
                // Path destination = Paths.get("MyFile.jpg");
                // Files.copy(inputStream, destination,
                // StandardCopyOption.REPLACE_EXISTING);
                System.out.println("found file in request:" + item);
            }else{
                System.out.println("found string in request:" + new String(item.get(), "UTF-8"));
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }

    return "200";
}

答案 1 :(得分:2)

我正在使用带有Restlet的Camel REST DSL,并且能够使用以下代码进行文件上传。

rest("/images").description("Image Upload Service")
.consumes("multipart/form-data").produces("application/json")
.post().description("Uploads image")
        .to("direct:uploadImage");

from("direct:uploadImage")
.process(new Processor() {

    @Override
    public void process(Exchange exchange) throws Exception {

        MediaType mediaType = 
            exchange.getIn().getHeader(Exchange.CONTENT_TYPE, MediaType.class);
        InputRepresentation representation =
            new InputRepresentation(
                exchange.getIn().getBody(InputStream.class), mediaType);

        try {
            List<FileItem> items = 
                new RestletFileUpload(
                    new DiskFileItemFactory()).parseRepresentation(representation);

            for (FileItem item : items) {
                if (!item.isFormField()) {
                    InputStream inputStream = item.getInputStream();
                    Path destination = Paths.get("MyFile.jpg");
                    Files.copy(inputStream, destination,
                                StandardCopyOption.REPLACE_EXISTING);
                }
            }
        } catch (FileUploadException | IOException e) {
            e.printStackTrace();
        }


    }

});

答案 2 :(得分:0)

即使您没有为restdsl组件使用restlet(例如jetty),也可以使用restdsl执行此操作。

你需要首先为该路线转换restdinding并重新设置两个类来处理你体内的多部分。

你需要两个班级:

  • DWRequestContext
  • DWFileUpload

然后在自定义处理器中使用它们

这是代码:

DWRequestContext.java

    import org.apache.camel.Exchange;
    import org.apache.commons.fileupload.RequestContext;

    import java.io.IOException;
    import java.io.InputStream;
    import java.nio.charset.StandardCharsets;

    public class DWRequestContext implements RequestContext {

        private Exchange exchange;

        public DWRequestContext(Exchange exchange) {
            this.exchange = exchange;
        }

        public String getCharacterEncoding() {
            return StandardCharsets.UTF_8.toString();
        }

        //could compute here (we have stream cache enabled)
        public int getContentLength() {
            return (int) -1;
        }

        public String getContentType() {
            return exchange.getIn().getHeader("Content-Type").toString();
        }

        public InputStream getInputStream() throws IOException {
            return this.exchange.getIn().getBody(InputStream.class);
        }
    }

DWFileUpload.java

    import org.apache.camel.Exchange;
    import org.apache.commons.fileupload.FileItem;
    import org.apache.commons.fileupload.FileItemFactory;
    import org.apache.commons.fileupload.FileUpload;
    import org.apache.commons.fileupload.FileUploadException;

    import java.util.List;

    public class DWFileUpload extends
            FileUpload {

        public DWFileUpload() {
            super();
        }

        public DWFileUpload(FileItemFactory fileItemFactory) {
            super(fileItemFactory);
        }

        public List<FileItem> parseInputStream(Exchange exchange)
                throws FileUploadException {
            return parseRequest(new DWRequestContext(exchange));
        }
    }

您可以像这样定义处理器:

    routeDefinition.process(new Processor() {
                    @Override
                    public void process(Exchange exchange) throws Exception {
                        // Create a factory for disk-based file items
                        DiskFileItemFactory factory = new DiskFileItemFactory();
                        factory.setRepository(new File(System.getProperty("java.io.tmpdir")));

                        DWFileUpload upload = new DWFileUpload(factory);

                        java.util.List<FileItem> items = upload.parseInputStream(exchange);

                        //here I assume I have only one, but I could split it here somehow and link them to camel properties...
                        //with this, the first file sended with your multipart replaces the body
                        // of the exchange for the next processor to handle it
                        exchange.getIn().setBody(items.get(0).getInputStream());
                    }
                });

答案 3 :(得分:0)

我偶然遇到了同样的要求,即必须通过Apache Camel Restlet组件使用多部分请求(包含文件数据,包括二进制文件)。 即使2.17.x已经发布,但由于我的项目是更广泛的框架/应用程序的一部分,所以我必须使用2.12.4版本。

最初,我的解决方案从restlet-jdbc示例中汲取了很多东西,以交换数据,虽然可以成功检索文本文件,但我无法检索正确的二进制内容。

我尝试将数据直接转储到文件中,以使用以下代码(节略的)检查内容。

from("restlet:/upload?restletMethod=POST")
.to("direct:save-files");

from("direct:save-files")
.process(new org.apache.camel.Processor(){
    public void process(org.apache.camel.Exchange exchange){
    /*
     * Code to sniff exchange content
     */
     }
 })
 .to("file:///C:/<path to a folder>");
 ;

我使用apache fileuplaod库中的org.apache.commons.fileupload.MultipartStream编写了以下实用程序类,以解析文件中的Multipart请求。当来自Postman的mulitpart请求的输出被提供给它时​​,它成功运行。但是,无法解析Camel创建的文件的内容(即使直觉到两个文件的内容看起来都相似)。

public class MultipartParserFileCreator{

    public static final String DELIMITER = "\\r?\\n";

    public static void main(String[] args) throws Exception {       
        // taking it from the content-type in exchange 
        byte[] boundary = "------5lXVNrZvONBWFXxd".getBytes();
        FileInputStream fis = new FileInputStream(new File("<path-to-file>"));  
        extractFile(fis, boundary);
    }

    public static void extractFile(InputStream is, byte[] boundary) throws Exception {
        MultipartStream multipartStream = new MultipartStream(is, boundary, 1024*4, null);
        boolean nextPart = multipartStream.skipPreamble();
        while (nextPart) {          
            String headers = multipartStream.readHeaders();
            if(isFileContent(headers)) {
                String filename = getFileName(headers);             
                File file = new File("<dir-where-file-created>"+filename);
                if(!file.exists()) {
                    file.createNewFile();
                }
                FileOutputStream fos = new FileOutputStream(file);                      
                multipartStream.readBodyData(fos);
                fos.flush();
                fos.close();
            }else {
                multipartStream.readBodyData(System.out);
            }
            nextPart = multipartStream.readBoundary();
        }
    }

    public static String[] getContentDispositionTokens(String headersJoined) {      
        String[] headers = headersJoined.split(DELIMITER, -1);
        for(String header: headers) {
            System.out.println("Processing header: "+header);
            if(header != null && header.startsWith("Content-Disposition:")) {
                return header.split(";");
            }
        }       
        throw new RuntimeException(
                String.format("[%s] header not found in supplied headers [%s]", "Content-Disposition:", headersJoined));

    }

    public static boolean isFileContent(String header) {        
        String[] tokens = getContentDispositionTokens(header);
        for (String token : tokens) {
            if (token.trim().startsWith("filename")) {
                return true;
            }
        }       
        return false;
    }

    public static String getFileName(String header) {
        String[] tokens = getContentDispositionTokens(header);
        for (String token : tokens) {
            if (token.trim().startsWith("filename")) {
                String filename =  token.substring(token.indexOf("=") + 2, token.length()-1);
                System.out.println("fileName is " + filename);
                return filename;
            }
        }       
        return null;
    }
} 

在通过Camel代码进行调试时,我注意到Camel在某一阶段将整个内容转换为String。过了一会儿,我不得不停止采用这种方法,因为适用于2.12.4版本的网上资源很少,我的工作也没有进展。

最后,我求助于以下解决方案

  1. 编写HttpServletRequestWrapper的实现以允许 输入流的多次读取。一个人可以从中得到一个想法 How to read request.getInputStream() multiple times
  2. 创建一个使用上述方法包装HttpServletRequest对象的过滤器,读取文件并将其提取到目录Convenient way to parse incoming multipart/form-data parameters in a Servlet中,然后使用request.setAttribute()方法将路径附加到请求。使用web.xml,在restlet servlet上配置此过滤器
  3. 在骆驼路线的处理方法中,键入强制 将HttpServletRequest对象中的exchange.getIn()。getBody()提取出来 属性(路径),用于将其作为ByteStreamArray读取,用于 进一步处理

不是最干净的,但是我可以实现目标。