2012-05-24 5 views
4

EJB 클라이언트 (JBoss)에서 큰 파일을 검색해야하는 EJB 클라이언트가 있습니다.원격 EJB 호출에서 거대한 파일 반환

이를 구현하는 가장 명백한 방법은 이와 같은 방법으로 EJB 외관을 제공하는 서버입니다 :

public byte[] getFile(String fileName); 

바이트 배열에 메모리에 전체 파일을로드, 을 의미하며, 그 다음이 바이트 배열을 전송합니다.

이 방법은 전체 파일을 메모리에로드하므로 파일이 너무 많아서 오버플로가 발생합니다.

이 문제를 해결할 수있는 옵션이 있습니까?

답변

1

RMI 프로토콜은 대용량 파일을 전송할 때 완전히 잘못된 솔루션입니다. 가능하지만 조용한 비효율적 인 해결책은 파일을 작은 덩어리로 나눠서 EJB에서 보내고 클라이언트 측에서 파일을 다시 어셈블하는 것입니다. 이런 식으로 뭔가 :

public class FileMetadata { 
    .... 
    private long chunkCount; 
    private long chunkSize; 
    .... 
} 

... 
public FileMetadata getFileMetadata(String fileName) {...} 
public byte[] getFileChunk(String fileName, long chunkNumber) {...} 
... 
5

HTTP는 더 나은 선택이 될 것입니다,하지만이 직렬화 트릭을 시도했다 :

import java.io.*; 

public class FileContent implements Serializable { 

    private transient File file; 

    public FileContent() { 
    } 

    public FileContent(File file) { 
     this.file = file; 
    } 

    private void writeObject(ObjectOutputStream out) throws IOException { 
     // 1. write the file name 
     out.writeUTF(file.getAbsolutePath()); 

     // 2. write the length 
     out.writeLong(file.length()); 

     // 3. write the content 
     final InputStream in = new BufferedInputStream(new FileInputStream(file)); 
     final byte[] buffer = new byte[1024]; 

     int length; 
     while ((length = in.read(buffer)) != -1) { 
      out.write(buffer, 0, length); 
     } 
     out.flush(); 
     in.close(); 
    } 

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 
     // 1. read the file name 
     final String path = in.readUTF(); 

     // 2. read the length 
     long remaining = in.readLong(); 

     // 3. read the content 
     file = new File(path); 
     final OutputStream out = new BufferedOutputStream(new FileOutputStream(file)); 

     final byte[] buffer = new byte[1024]; 

     while (true) { 
      int length = in.read(buffer, 0, (int) Math.min(remaining, buffer.length)); 
      if (length == -1) break; 

      out.write(buffer, 0, length); 

      remaining -= length; 
      if (remaining <= 0) break; 
     } 
     out.flush(); 
     out.close(); 
    } 
} 
+1

은 가능한 경우 HTTP가 더 나은 옵션이 될 것이라는 데 동의했지만, 귀하가 가진 Externalizable 솔루션은 꽤 좋았으며 실제로 잘 작동하는 것으로 보았습니다. 파일의 내용은 메모에 나와 있듯이 메모리에 저장되지 않습니다. 위의 코드는 메모리 문제를 일으키지 않고 임의 크기의 파일을 효율적으로 스트리밍합니다. IO 스트림이 닫히지 않도록 try/finally가 부족합니다. –

+0

내가 대답 한 것처럼 [rmiio] (http://openhms.sourceforge.net/rmiio/) 라이브러리는이 개념을보다 성숙한 구현 방식으로 제공합니다 (File뿐 아니라 모든 InputStream으로 일반화되어 있습니다). 즉석 압축 지원). 이 예제의 또 다른 문제점은 클라이언트와 서버에서 동일한 파일 이름이 유효하다고 가정한다는 것입니다. – jtahlborn

+0

@jtahlborn 메모 주셔서 감사. 나는 RMIIO에 대해 인식하지 못했다. 환상적인 도서관처럼 들린다. 내 Externalizable 메모가 오래된 것 같습니다. 이것은 JDK 1.2 일 안에 다시 돌아가는 방식입니다.그들이 어떤 점에서 그것을 제거한 것처럼 보입니다. 그것은 환상적입니다. –

2

BytearrayOutputStream을 래핑 한 ZipOutputStream을 사용할 수 있습니다. 이 액션을 사용하면 RMI 전송의 오버 헤드를 줄이기 위해 메소드 선언을 사용하여 압축 된 바이트 배열을 반환 할 수 있습니다.

0

아키텍처를 다시 생각해 보겠습니다. ejb 호출을 빠르게 유지해야합니다. ejb 호출에서 파일 위치를 반환 한 다음 다른 절차에서 다운로드를 처리하는 방법에 대한 자세한 내용은 여기에서 다운로드 방법을 참조하십시오. 큰 파일 How to download and save a file from Internet using Java?

0

내가했다 접근 방식은 (마음 만 @LocalBean의 작동)이 EJB에서 File.createTempFile로 작성하고 데이터를 검색하고 클라이언트에서 삭제하는 File을 반환하는 것입니다. Path도 사용할 수 있지만 Serializable이 아닙니다.

그러나 HTTP 서버가 호스팅하는 파일에 기록하고 나중에 HTTP 서버에서 검색 할 수도 있습니다. 그런 다음 나중에 파일을 삭제하라는 HTTP DELETE 요청을 보냅니다. EJB가 대신 java.net.URI을 리턴합니다.

관련 문제