从远程EJB调用返回大文件

时间:2012-05-24 08:58:40

标签: java java-ee streaming ejb rmi

我有一个EJB客户端,需要从EJB服务器(JBoss)检索大文件。

实现这一点的明显方法是使用类似这样的方法提供EJB外观的服务器:

public byte[] getFile(String fileName);

这意味着,在内存中加载整个文件,在一个字节数组中,然后在线上发送这个字节数组。

问题是这种方法将整个文件加载到内存中,并且由于文件很大,它会溢出它。

有没有办法解决这个问题?

6 个答案:

答案 0 :(得分:6)

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 :(得分:5)

RMIIO库是针对这种情况而构建的。它甚至包括@DavidBlevins解决方案的实现,DirectRemoteInputStream

答案 2 :(得分:2)

您可以使用ZipOutputStream,它包含在BytearrayOutputStream中。此操作将允许您首先使用方法声明返回压缩字节数组,以减少RMI传输的开销。

答案 3 :(得分:1)

RMI协议是发送大文件的完全错误的解决方案。一个可能但安静的低效解决方案是将文件分成小块,从EJB发送它们并在客户端重新组装文件。像这样:

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

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

答案 4 :(得分:0)

我会重新考虑你的架构,恕我直言你应该快速保持你的ejb调用,如何从ejb调用返回文件的位置,然后在另一个程序中处理downlod,看看这里有关如何下载的更多信息大文件How to download and save a file from Internet using Java?

答案 5 :(得分:0)

我采用的方法(介意你只适用于@LocalBean)是从EJB写入File.createTempFile并返回File来检索数据并从中删除它客户。也可以使用Path,但请注意,它不是Serializable

但是,您也可以将其写入由HTTP服务器托管的文件,并在以后从那里检索它。然后发送HTTP DELETE请求以删除该文件。您的EJB将返回java.net.URI