无法在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)。
你有两个选择:
- 好的选择:以块的forms加载文件(例如,一次1024个字节)并像这样发送它。 实施需要更多的努力。
- 错误选项:通过-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()
时显然会耗尽内存。 此方法仅适用于您知道数据真的非常小的情况。
- java.text.SimpleDateFormat不是线程安全的
- 如何从POST请求中获取XML并在Servlet Filter中修改它?
- Maven的档案相当于Gradle
- 在构造函数中调用虚方法:Java和C ++之间的区别
- 设置/配置EJB Timer Service的DataSource
- 使用java识别mp3结尾处的沉默
- 外部和内部类方法之间的锁定和同步?
- AspectJ切入特定方法中的方法调用
- ClassNotFoundException:org.hibernate.hql.internal.ast.HqlToken甚至在添加了classic.ClassicQueryTranslatorFactory 之后