【问题标题】:Sending big file using FileInputStream/ObjectOutputStream使用 FileInputStream/ObjectOutputStream 发送大文件
【发布时间】:2012-06-04 21:05:39
【问题描述】:

我的作业需要帮助,任何帮助将不胜感激。我可以毫无问题地发送小文件。但是当我尝试发送时,假设一个 1GB 文件字节数组发送 OutOfMemoryError,所以我需要一个更好的解决方案来将文件从服务器发送到客户端。如何改进此代码并发送大文件,请帮助我。

服务器代码:

    FileInputStream fis = new FileInputStream(file);
    byte[] fileByte = new byte[fis.available()]; //This causes the problem.
    bytesRead = fis.read(fileByte);
    oos = new ObjectOutputStream(sock.getOutputStream());
    oos.writeObject(fileByte);

客户代码:

    ois = new ObjectInputStream(sock.getInputStream());
    byte[] file = (byte[]) ois.readObject();
    fos = new FileOutputStream(file);
    fos.write(file);

【问题讨论】:

  • "sends out of memory exception" Java 中没有这样的东西,DYM OutOfMemoryError?如果是,请具体说明,如果不是,请说明(也具体说明)。
  • 顺便说一句-您的第一个代码似乎暗示输入文件始终包含序列化对象。是这样吗?
  • 哦,对不起,OutOfMemoryError 也不例外。而且我不知道如果文件包含序列化对象,服务器发送客户端请求的任何文件是什么意思。
  • ObjectOutputStreamObjectInputStream 用于序列化对象。如果内容是“任何旧字节”,则代码不应使用它们。
  • 好的,我会这样做,谢谢你,我是新来的。

标签: java bytearray inputstream outputstream


【解决方案1】:

只需将数组拆分为更小的块,这样您就不需要分配任何大数组。

例如,您可以将数组拆分为 16Kb 的块,例如 new byte[16384],然后一一发送。在接收端,您必须等到一个块可以被完全读取,然后将它们存储在某个地方并从下一个块开始。

但是,如果您无法在服务器端分配所需大小的整个数组,您将无法存储您将要接收的所有数据。

您还可以在发送数据之前对其进行压缩以节省带宽(和时间),请查看ZipOutputStreamZipInputStream

【讨论】:

    【解决方案2】:

    不要将整个文件读入内存,使用小缓冲区并在读取文件时写入:

    BufferedOutputStream bos = new BufferedOutputStream(sock.getOutputStream())
    
    File file = new File("asd");
    FileInputStream fis = new FileInputStream(file);
    BufferedInputStream bis = new BufferedInputStream(fis);
    byte[] buffer = new byte[1024*1024*10];
    int n = -1;
    while((n = bis.read(buffer))!=-1) {
      bos.write(buffer,0,n):
    }
    

    使用 Buffered* 优化 Streams 的写入和读取

    【讨论】:

    • 非常感谢我会试试这个。
    【解决方案3】:

    我是这样解决的:

    客户代码:

     bis=new BufferedInputStream(sock.getInputStream());
     fos = new FileOutputStream(file);
     int n;
     byte[] buffer = new byte[8192];
     while ((n = bis.read(buffer)) > 0){
     fos.write(buffer, 0, n);}
    

    服务器代码:

     bos= new BufferedOutputStream(sock.getOutputStream());
     FileInputStream fis = new FileInputStream(file);
     BufferedInputStream bis = new BufferedInputStream(fis);
     int n=-1;
     byte[] buffer = new byte[8192];
     while((n = bis.read(buffer))>-1) 
     bos.write(buffer,0,n);
    

    【讨论】:

    • 我按照这个例子,在接收端的while循环中,我打印“Writing file...”。我看到的是接收器最终陷入无限循环并且永远不会完成接收。我可以看到它收到的文件,并且文件大小最终会达到它应该有的大小,然后它会停止,但客户端永远不会停止思考它正在接收的数据。如果我关闭接收器并尝试打开文件,它已损坏。
    【解决方案4】:

    根据您是否必须自己编写代码,现有的库可以解决此问题,例如rmiio。如果你不使用 RMI,只是简单的 java 序列化,你可以使用DirectRemoteInputStream,它有点像 Serializable InputStream。 (这个库还支持自动压缩数据等功能)。

    实际上,如果您发送文件数据,最好放弃 Object 流并使用 DataInput/DataOutput 流。首先写入一个指示文件长度的整数,然后将字节直接复制到流中。在接收端,读取整数文件长度,然后准确读取那么多字节。

    当您在流之间复制数据时,使用一个小的、固定大小的 byte[] 在循环中在输入和输出流之间移动数据块。网上有很多关于如何正确执行此操作的示例(例如@ErikFWinter 的回答)。

    【讨论】:

      猜你喜欢
      • 2015-11-17
      • 1970-01-01
      • 1970-01-01
      • 2012-02-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多