无法在Java中通过套接字发送大文件

时间:2012-07-12 09:58:27

标签: java sockets

我得到了服务器和客户端应用程序,它们在发送小文件的同时工作得很好,但是当我尝试发送例如700mb的电影文件时它给了我

Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: Java heap space

我搜索了互联网并发现了一些关于发送大文件的教程,但是对它们不太了解,但我认为我的问题是写文件。

这是服务器用来编写我的文件的代码:

output = new FileOutputStream(directory + "/" + fileName);
            long size = clientData.readLong();
            byte[] buffer = new byte[1024];

            while (size > 0 && (bytesRead = clientData.read(buffer, 0, (int) Math.min(buffer.length, size))) != -1) {
                output.write(buffer, 0, bytesRead);
                size -= bytesRead;
            }
            output.close();

以下是我的客户端用来发送文件的代码:

byte[] fileLength = new byte[(int) file.length()];  

        FileInputStream fis = new FileInputStream(file);  
        BufferedInputStream bis = new BufferedInputStream(fis);

        DataInputStream dis = new DataInputStream(bis);     
        dis.readFully(fileLength, 0, fileLength.length);  

        OutputStream os = socket.getOutputStream();  

        //Sending size of file.
        DataOutputStream dos = new DataOutputStream(os);   
        dos.writeLong(fileLength.length);
        dos.write(fileLength, 0, fileLength.length);     
        dos.flush();  

        socket.close();  

5 个答案:

答案 0 :(得分:7)

它为您提供OutOfMemoryError,因为您在发送之前尝试将整个文件读入内存。这是完全100%完全没必要的。只需读取和写入块,就像在接收代码中一样。

答案 1 :(得分:4)

问题是你试图将整个文件一次加载到内存中(通过readFully),这超出了你的堆空间(我认为默认值是256mb)。

您有两种选择:

  1. 好的选择:以块的形式加载文件(例如,一次1024个字节)并像这样发送它。需要付出更多努力才能实施。
  2. 错误选项:通过-Xmx选项增加堆空间。不推荐,我大多提到这个,以防万一你需要为程序增加一点堆空间,但在这种情况下,这是一个非常糟糕的主意。
  3. 对于选项一,您可以尝试以下方式:

    DataInputStream in = new DataInputStream(new FileInputStream("file.txt"));
    byte[] arr = new byte[1024];
    try {
        int len = 0;
        while((len = in.read(arr)) != -1)
        {
            // send the first len bytes of arr over socket.
        } 
    } catch(IOException ex) {
        ex.printStackTrace();
    }
    

答案 2 :(得分:2)

当程序达到堆大小限制时,抛出

OutOfMemoryError。您将整个文件加载到RAM中。默认堆大小是物理内存大小的1/4或1GB,因此您的程序达到了限制(您可能有2GB RAM,因此堆大小为512MB)。

您应该以块(例如10MB)读取文件,因此您将无法达到堆大小限制,并且可以在出现某些错误时重新发送块,而不是重新发送整个文件。你甚至可以在不同的线程中读取块,所以当你发送1块时,你可以加载第二块,当你发送第一块时,你可以立即开始发送第二块。

答案 3 :(得分:1)

问题是在您的客户端中,您为整个文件创建了一个字节数组。

byte[] fileLength = new byte[(int) file.length()];  //potentially huge buffer allocated here

您应该在服务器端执行相同的操作,并将块中的文件块读入固定大小的缓冲区。

答案 4 :(得分:0)

就像你在服务器端使用缓冲区一样发送数据,使用applet / client中的缓冲区来读取它。如果您要传输大文件,则在使用readFully()时显然会耗尽内存。此方法仅适用于您知道数据 真的 小的情况。