Java - NIO通道传输大文件

6

我需要使用NIO通过ServerSocket传输大约100MB的数据,但我无法想出如何在不中断传输的情况下保持传输状态。

我的第一个想法是发送文件的大小,但显然我无法发送这么大的文件大小,因为它一次性无法放入RAM中。然后我想,为什么不一直传输直到没有接收到数据,但这就是问题所在。

即使我一直在写服务器端的数据

        FileChannel fc = new FileInputStream(f).getChannel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        while(fc.read(buffer) > 0) {
            buffer.flip();
            while(channel.write(buffer) > 0);
            buffer.clear();
        }

但是因为文件传输必须要有间断,所以不停地读取数据并在没有数据可用时中断是一个不好的想法。

我无法想象如何在不必发送每个片段的数据包和操作代码的情况下告诉客户端是否仍然有可用数据。这是否可能?

我还在想是否有更好的方法来发送整个缓冲区,而不是像下面这样。

while(channel.write(buffer) > 0);

我已经回答了部分问题,但是我必须说我不理解你所说的“文件传输中必须有间断”和“持续读取数据并在没有可用数据时中断是一个坏主意”的含义。请解释一下。 - user207421
3个回答

5
也许您正在寻找这个:

也许您正在寻找这个:

channel.transferFrom(0, fc.size(), fc);

哇,我怎么会错过那个?我明天会尝试一下,如果它能正常工作的话,我会接受你的答案。 - Ruuhkis
@Ruuhkis请注意,您需要在循环中调用此方法,因为不能保证一次调用会传输整个长度。另请参阅我的答案。 - user207421

4
正确的通过缓冲区复制通道的方法如下:
while (in.read(buffer) >= 0 || buffer.position() > 0)
{
  buffer.flip();
  out.write(buffer);
  buffer.compact();
}

这将解决所有边缘情况,包括读取长度与写入长度不相等以及在输入末尾有剩余数据的情况。

请注意,这是针对阻塞模式的。如果您处于非阻塞模式,则应在read()或write()返回零时返回到select()循环。


希望我可以接受多个答案,因为我不确定这个接受属于谁。Vizier 给了我一个完美的解决方案,但你实现了它。我目前正在使用几乎与你的代码完全相同的代码,它运行得非常完美。非常感谢你们两个。但我不确定紧凑操作是什么,但我猜它一定比清除更好。再次感谢! - Ruuhkis
1
@Ruuhkis的compact()函数会删除已经写入的内容,但是保留未被写入的内容。如果你使用clear()函数,那么当读取长度!=写入长度时就有可能会丢失数据,这种情况随时都可能发生。 - user207421

0

如果您不能依赖套接字保持打开状态,那么在您的协议中构建恢复和继续机制是不可避免的。至于告诉客户端要期望多少字节,您可以使用 File.length 在不将文件读入内存的情况下找到文件的大小。


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