Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/307.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 从远程EJB调用返回大型文件_Java_Jakarta Ee_Streaming_Ejb_Rmi - Fatal编程技术网

Java 从远程EJB调用返回大型文件

Java 从远程EJB调用返回大型文件,java,jakarta-ee,streaming,ejb,rmi,Java,Jakarta Ee,Streaming,Ejb,Rmi,我有一个EJB客户端,它需要从EJB服务器(JBoss)检索一个大文件 实现这一点的明显方式是服务器提供一个EJB外观,其方法如下: public byte[] getFile(String fileName); 这意味着,将整个文件加载到内存中,以字节数组的形式,然后通过线路发送该字节数组 问题是,这种方法将整个文件加载到内存中,由于文件很大,它会使其溢出 有什么办法可以解决这个问题吗?RMI协议对于发送大文件来说是一个非常糟糕的解决方案。一个可能但效率低下的解决方案是将文件分成小块,从EJ

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

实现这一点的明显方式是服务器提供一个EJB外观,其方法如下:

public byte[] getFile(String fileName);
这意味着,将整个文件加载到内存中,以字节数组的形式,然后通过线路发送该字节数组

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


有什么办法可以解决这个问题吗?

RMI协议对于发送大文件来说是一个非常糟糕的解决方案。一个可能但效率低下的解决方案是将文件分成小块,从EJB发送,然后在客户端重新组装文件。大概是这样的:

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

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

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();
    }
}
import java.io.*;
公共类FileContent实现可序列化{
私有瞬态文件;
公共文件内容(){
}
公共文件内容(文件){
this.file=文件;
}
私有void writeObject(ObjectOutputStream out)引发IOException{
//1.写入文件名
writeUTF(file.getAbsolutePath());
//2.写下长度
out.writeLong(file.length());
//3.写下内容
final InputStream in=new BufferedInputStream(new FileInputStream(file));
最终字节[]缓冲区=新字节[1024];
整数长度;
而((长度=英寸读取(缓冲区))!=-1){
out.write(缓冲区,0,长度);
}
out.flush();
in.close();
}
私有void readObject(ObjectInputStream in)引发IOException、ClassNotFoundException{
//1.读取文件名
最终字符串路径=in.readUTF();
//2.阅读长度
剩余时间长=in.readLong();
//3.阅读内容
文件=新文件(路径);
final OutputStream out=new BufferedOutputStream(new FileOutputStream(file));
最终字节[]缓冲区=新字节[1024];
while(true){
int length=in.read(buffer,0,(int)Math.min(剩余,buffer.length));
如果(长度==-1)中断;
out.write(缓冲区,0,长度);
剩余-=长度;

如果(剩余的该库正是针对这种情况而构建的。它甚至包括@DavidBlevins解决方案的实现。

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

我会重新考虑您的体系结构,我想您应该如何保持ejb调用的快速,如何从ejb调用返回文件的位置,然后在另一个过程中处理downlod,在这里查看有关如何下载大文件的更多信息

我采取的方法(请注意,您仅适用于
@LocalBean
s)是从EJB写入
文件.createTempFile
,并返回
文件
,以检索数据并从客户端删除它。
路径
也可以使用,但请注意它不是
可序列化的


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

同意,在可能的情况下,HTTP将是一个更好的选择,但您拥有的外部化解决方案eems非常好,我看到它在实践中工作得很好。正如您的说明所暗示的,文件的内容从未保存在内存中。上面的代码可以有效地流式处理任意大小的文件,而不会导致内存问题。它确实缺少确保IO流关闭的try/finally。正如我在回答中提到的,库有一个这个概念的更成熟的实现(它被推广到任何InputStream,而不仅仅是一个文件,并且包括动态压缩支持)。此示例的另一个问题是,它假定相同的文件名在客户端和服务器上有效。@jtahlborn感谢您的注释。我不知道RMIO,听起来像是一个很棒的库。看起来我的可外部化注释过时了。这是JDK 1.2天的工作方式。看起来他们在某个时候删除了它,而我太棒了。