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

我得到了服务器和客户端应用程序,它们在发送小文件时工作得很完美,但当我尝试发送例如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(); 

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

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

你有两个选择:

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

对于选项一,您可以尝试以下方法:

 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(); } 

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

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

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

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

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

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