使用套接字编程发送大文件

4

我想知道是否能得到一些解决问题的指导...

你看,我正在尝试进行套接字编程;我成功创建了一个客户端和服务器;服务器可以轻松地向客户端写入信息; 甚至我已经成功发送了文件。

byte [] mybytearray  = new byte [(int)myFile.length()];
  FileInputStream fis = new FileInputStream(myFile);

在服务器端实现。 在客户端实现。
byte [] mybytearray  = new byte [filesize];
InputStream is = sock.getInputStream();
FileOutputStream fileos = new FileOutputStream("Filename.dat");
BufferedOutputStream bufferos = new BufferedOutputStream(fileos);
bytesRead = is.read(mybytearray,0,mybytearray.length);
current = bytesRead;


do {
   bytesRead =
      is.read(mybytearray, current, (mybytearray.length-current));
   if(bytesRead >= 0) current += bytesRead;
} while(bytesRead > -1);

bufferos.write(mybytearray, 0 , current);
bufferos.flush();
long end = System.currentTimeMillis();
System.out.println(end-start);
bufferos.close();

我的问题是我无法发送大文件,总是收到“Exception in thread "main" java.lang.OutOfMemoryError: Java heap space”的错误提示。

有什么想法或方向可以帮助我从服务器向客户端发送大文件吗? 我的意思是像600 MB这样的大小...... 有什么建议吗? 非常感谢...谢谢

4个回答

5

你的堆当前正在尝试容纳“mybytearray”,这是你正在尝试接收的文件的大小(可能是这样)。你需要将bos.write()操作移到循环内部,并使“mybytearray”成为固定大小。以下是一个复制流到另一个流的示例,它不太关心被流传输的数据的大小:

    public static void stream(InputStream in, OutputStream out)
        throws IOException {
    byte[] buf = new byte[1024];
    int bytesRead = 0;

    try {

        while (-1 != (bytesRead = in.read(buf, 0, buf.length))) {
            out.write(buf, 0, bytesRead);
        }

    } catch (IOException e) {
        log.error("Error with streaming op: " + e.getMessage());
        throw (e);
    } finally {
                    try{
           in.close();
           out.flush();
           out.close();
                    } catch (Exception e){}//Ignore
    }
}

如果发生IOException,则InputStream和OutputStream都会保持打开状态。最好在finally块中关闭它们(吞咽异常)。 - Adamski
1
可能有第三方库来处理这个问题,但我通常会实现一个实用方法:IOUtil.closeIgnoringExceptions(Closeable ... closeables),以执行此步骤并避免需要在第一个finally块中嵌套第二个try ... finally。该实用程序方法应处理空引用,以避免其中一个流在创建过程中抛出异常的情况。 - Adamski
像魔法一样运行了;我根据你的示例应用了解决方案,像魔法一样工作了...谢谢大家... - ibininja

4
由于你分配了mybytearray长度为filesize,所以你的内存不足。一种可能的解决方法是使用更大的堆来调用JVM;例如 -Xmx1G。然而,另一个问题是是否需要将整个文件读入内存——在服务器端这绝对不是必要的: 你可以将文件按块读取到一个固定大小的数组中,然后将这些块写入输出流。这样,当服务器试图发送大文件回客户端时,它的内存占用量不会增加。

1
是的 - 绝对可以只读入适当大小的块并发送它们。512kb大小的块已经足够了。即使您增加了堆大小,如果要发送的文件接近堆大小(但没有超过),而您希望此服务器能够服务多个同时连接,则一旦打开第二个大文件,您将遇到相同的问题。 - jefflunt
3
增加堆大小是解决这个问题的可怕方法。 - Mick Sear
1
@normalocity:同意;我关于堆大小的观点更多地是针对客户端,因为客户端试图将文件从服务器读入数组。 - Adamski
@Mick:你完全忽略了第二段吗? - Adamski
@Adamski - 是的,我明白了 - 我完全支持你的答案。我的观点更多是为了ibininja的利益。 - jefflunt
显示剩余5条评论

3

为了发送文件,不要将整个文件读入内存中,而是分块发送。

byte [] mybytearray  = new byte [(int)myFile.length()];

如果 (int)myFile.length() 的大小超出了内存限制,那就不太好了。

相反,可以使用下面的方式:

byte [] buffer= new byte[4096];

或者这样做,制作一个循环,从文件中读取并发送到套接字。

谢谢;目前正在研究如何分块发送文件。 - ibininja

-2

这意味着JVM内存不足。因此,您应该增加JVM堆空间。所以我认为问题不在于发送大文件。


3
-1 是因为有更好的方法来解决这个问题。如果他使用当前的解决方案发送比堆空间更大的文件,他将始终会耗尽内存。 - Mick Sear
是的,你说得对,其他人建议的解决方案要好得多。将大文件分成固定大小的内存数组更有意义 :) - Fatih Donmez

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接