【问题标题】:Accept Multipart file upload as camel restlet or cxfrs endpoint接受作为骆驼restlet或cxfrs端点的分段文件上传
【发布时间】:2015-01-07 00:47:36
【问题描述】:

我希望实现一个路由,其中​​ 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 "Content-Type: multipart/form-data" -F "image=@/Users/navaltiger/1.jpg; type=image/jpg" http://:8080/JettySample /骆驼/你好

以下代码有效(但不能使用,因为它嵌入了码头,我们想将其部署在 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));
                }

【问题讨论】:

    标签: apache-camel restlet


    【解决方案1】:

    我遇到了类似的问题,并在 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/文本+文件),并且显然标准解组过程无法处理请求,因为它需要一个字符串正文而不是多部分有效负载。

    文件和其他参数是从使用文件上传角度模块的前端发送的: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";
    }
    

    【讨论】:

      【解决方案2】:

      我将 Camel REST DSL 与 Restlet 一起使用,并且能够使用以下代码进行文件上传。

      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();
              }
      
      
          }
      
      });
      

      【讨论】:

        【解决方案3】:

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

        您需要先为该路线打开 restdinding 并创建两个类来处理您体内的多部分。

        你需要两个类:

        • DWRequestContext
        • DW文件上传

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

        这里是代码:

        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());
                            }
                        });
        

        【讨论】:

          【解决方案4】:

          我偶然发现了必须通过 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 的多部分请求的输出被馈送到它时,它成功地工作了。但是,无法解析由 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 将整个内容转换为字符串。过了一段时间,我不得不停止采用这种方法,因为网上几乎没有适用于 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. 在骆驼路线的工艺方法中,类型铸造 exchange.getIn().getBody() 在 HttpServletRequest 对象中,提取 属性(路径)使用它来读取文件作为 ByteStreamArray 进一步处理

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

          【讨论】:

            猜你喜欢
            • 2023-04-05
            • 1970-01-01
            • 2014-11-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-12-05
            相关资源
            最近更新 更多