2012-11-28 4 views
3

I posted이 질문은 행운을없이 CXF 목록에 표시됩니다. 그래서 여기에 우리가 간다. 큰 파일을 원격 서버에 업로드하려고합니다 (가상 시스템 디스크라고 생각할 수 있습니다). 그래서 나는 업로드 요청을 받아들이는 편안한 서비스를하고있다. 이 코드에 대한CXF jax-rs에서 멀티 포트 캐싱 사용 안 함

@POST 
@Consumes(MediaType.MULTIPART_FORM_DATA) 
@Path("/doupload") 
public Response receiveStream(MultipartBody multipart) { 
    List<Attachment> allAttachments = body.getAllAttachments(); 
    Attachment att = null; 
    for (Attachment b : allAttachments) { 
     if (UPLOAD_FILE_DESCRIPTOR.equals(b.getContentId())) { 
      att = b; 
     } 
    } 
    Assert.notNull(att); 
    DataHandler dh = att.getDataHandler(); 
    if (dh == null) { 
     throw new WebApplicationException(HTTP_BAD_REQUEST); 
    } 
    try { 
     InputStream is = dh.getInputStream(); 
     byte[] buf = new byte[65536]; 
     int n; 
     OutputStream os = getOutputStream(); 
     while ((n = is.read(buf)) > 0) { 
      os.write(buf, 0, n); 
     } 
     ResponseBuilder rb = Response.status(HTTP_CREATED); 
     return rb.build(); 
    } catch (IOException e) { 
     log.error("Got exception=", e); 
     throw new WebApplicationException(HTTP_INTERNAL_ERROR); 
    } catch (NoSuchAlgorithmException e) { 
     log.error("Got exception=", e); 
     throw new WebApplicationException(HTTP_INTERNAL_ERROR); 
    } finally {} 

} 

클라이언트는 매우 간단하다 : 업로드에 대한 처리기처럼 보이는

public void sendLargeFile(String filename) { 
    WebClient wc = WebClient.create(targetUrl); 
    InputStream is = new FileInputStream(new File(filename)); 
    Response r = wc.post(new Attachment(Constants.UPLOAD_FILE_DESCRIPTOR, 
     MediaType.APPLICATION_OCTET_STREAM, is)); 
} 

코드는 기능면에서 잘 작동합니다. 성능면에서 볼 때, 처리기 (receiveStream() 메서드)가 스트림에서 첫 번째 바이트를 가져 오기 전에 전체 스트림이 실제로 CachedOutputStream을 사용하여 임시 파일에 저장된다는 사실을 발견했습니다. 불행히도, 이것은 제 목적에는 용납되지 않습니다.

  • 내 처리기는 들어오는 바이트를 백엔드 스토리지 시스템 (가상 시스템 디스크 저장소)으로 전달하고 전체 디스크를 다시 캐시에 기록하기를 기다리는 데는 많은 시간이 걸립니다. 많은 자원과 처리량 감소.
  • 블로그를 작성하고 다시 읽는 데 드는 비용은 앱이 클라우드에서 실행 중이며 클라우드 제공 업체가 블록 당 읽기/쓰기 요금을 부과하기 때문에 발생합니다.
  • 모든 바이트가 로컬 디스크에 기록되므로 내 서비스 VM에는 업로드되는 모든 스트림의 총 크기를 수용 할 수있는 충분한 디스크 공간이 있어야합니다 (즉, 각 업로드 크기가 10GB 인 경우 1TB의 디스크가 있어야합니다. 콘텐츠를 캐시하기 만하면됩니다.) 이는 서비스 VM의 크기가 급격히 증가함에 따라 추가 비용이 발생하고 클라우드 공급자가 프로비저닝 된 디스크 크기에 대해서도 과금합니다.

이 모든 것을 감안할 때 HTTP InputStream (또는 최대한 가깝게)을 사용하여 첨부 파일을 거기에서 직접 읽고 나중에 처리하는 방법을 찾고 있습니다. 나는 하나의 질문을 짐작하는 것 같아요 :- CXF에게 CXF에게 출력 스트림 (내가 쓰는 것)을 사용하는 대신 CachedOutputStream을 사용하는 것입니다.

나는 비슷한 질문을 발견했다 here. 해상도는 CXF 2.2.3 이상을 사용하고 있으며 2.4.4를 사용하고 있으며 2.7.x로 시도했지만 운이 없다고합니다.

감사합니다.

답변

2

논리적으로 불가능하다고 생각합니다 (CXF 또는 그 밖의 다른 곳에서는 불가능합니다). getAllAttachements()을 호출하고 있습니다. 즉, 서버는 HTTP 입력 스트림에서 해당 정보를 수집해야합니다. 이는 전체 스트림이 MIME 구문 분석을 위해 메모리에 저장되어야 함을 의미합니다. 귀하의 경우에는

당신은 자신을 구문 분석 MIME을 스트림에 직접 작업하고 수행해야합니다

public Response receiveStream(InputStream input) { 

지금은 입력의 모든 권한을 가지고 있으며 메모리에 바이트 단위로 그것을 소비 할 수 있습니다.

+0

감사합니다. 나는 네가 뭔가있는 것 같아. 그러나, 내가 getAllAttachments()를 호출 할 때 내 처리기에 들어올 때가되면 모든 메시지가 이미 읽고 버퍼링됩니다. 그래도 나 한테 아이디어를 줬어. 이 동작을 일으키는 MIME 형식이라고 생각합니다. 핸들러에 InputStream을 전달하는 또 다른 MIME 유형을 사용할 수 있는지 확인하려고합니다. 다시 한 번 감사 드리며, 저에게 뭔가 도움이됩니다. –

0

나는 문제를 어색한 방식으로 고치려고했으나 효과가있어 내 경험을 공유하고 싶었습니다. "표준"또는 더 나은 방법이 있다면 알려주십시오.

서버 측에 글을 올리면서 보내지는 순서대로 모든 첨부 파일에 액세스하고 있음을 알았습니다.그래서, 위의 핸들러 메소드 (receiveStream() 메소드)의 동작을 반영하기 위해 서버 측에서 "@SequentialAttachmentProcessing"이라는 새로운 주석을 작성하고 위 메소드에 주석을 달았습니다.

또한 연결 목록과 같은 역할을하는 SequentialAttachment라는 Attachment의 하위 클래스를 작성했습니다. 그것은 현재 첨부 파일을 건너 뛰는 skip() 메서드를 가지고 있으며, 첨부 파일이 끝나면 hasMore() 메서드는 다른 것이 있는지 여부를 알려줍니다.

다음과 같이 동작하는 사용자 지정 다중 파트/양식 데이터 공급자를 작성했습니다. 대상 메서드에 위와 같이 주석이 첨부 된 경우 첨부 파일을 처리하고 그렇지 않으면 처리를 위해 기본 공급자를 호출합니다. 공급자가 처리 할 때 항상 최대 하나의 첨부 파일 만 반환합니다. 따라서 의심스럽지 않은 취급 방법을 오도 할 수 있습니다. 그러나 서버의 작성자가 메서드를 "@SequentialAttachmentProcessing"으로 주석 처리 했으므로 그 내용이 무엇인지 알아야하기 때문에 받아 들일 수 있다고 생각합니다.

@POST 
@SequentialAttachmentProcessing 
@Consumes(MediaType.MULTIPART_FORM_DATA) 
@Path("/doupload") 
public Response receiveStream(MultipartBody multipart) { 
    List<Attachment> allAttachments = body.getAllAttachments(); 
    Assert.isTrue(allAttachments.size() <= 1); 
    if (allAttachment.size() > 0) { 
     Attachment head = allAttachments.get(0); 
     Assert.isTrue(head instanceof SequentialAttachment); 
     SequentialAttachment att = (SequentialAttachment) head; 
     while (att != null) { 
      DataHandler dh = att.getDataHandler(); 
      InputStream is = dh.getInputStream(); 
      byte[] buf = new byte[65536]; 
      int n; 
      OutputStream os = getOutputStream(); 
      while ((n = is.read(buf)) > 0) { 
       os.write(buf, 0, n); 
      } 
      if (att.hasMore()) { 
       att = att.next(); 
      } 
     } 
    } 
} 

이 내 즉각적인 문제를 해결하는 동안, 나는 아직도이 일을하는 표준 방법이있을 가지고 있다고 생각합니다 : 그 결과

receiveStream() 메소드의 구현은 지금 같은 것입니다. 나는 이것이 누군가를 돕기를 바랍니다.

관련 문제