文件下载后while循环不退出

我有以下代码来下载通过 TCP 传输的文件:

        try (OutputStream out = new FileOutputStream(path); InputStream is = socket.getInputStream();) {
            byte[] bytes = new byte[1024];
            int count, xp = 0;
            while ((count = is.read(bytes)) > 0) {      // TODO after upload the service doesn't leave while loop
                out.write(bytes, 0, count);
            }
            System.out.println("hello");
         ...

上传代码:

if (ready.equalsIgnoreCase(CdnResponse.READY.getContext())){
    int read = 0;
    byte[] bytes = new byte[1024];
    while ((read = inputStream.read(bytes)) != -1) {
        out.write(bytes, 0, read);
    }

}

上传退出循环正常。

一旦处理完所有字节(它们总是被成功处理,但是循环永远不会退出),就会创建文件,没有任何问题,但是循环不会退出。

rzwitserloot

TCP/IP 连接被设计为长期流连接(建立在无序、无保证、基于数据包的 IP 协议之上)。

这意味着is.read(bytes)它完全符合规范所说的:它将等待至少 1 个字节可用,或者“流结束”信号进来。只要两者都不发生(没有字节到达,但流不是关闭),它将尽职尽责地阻止。如果必须的话,永远。

解决方案是 [A] 预先发送文件的大小,然后调整循环以在收到该数量的字节后退出,或者 [B] 关闭流。

要关闭流,请关闭套接字。听起来你不想这样做(你在流上多路复用多个东西,即在传输文件后,你可以发送其他命令)。

所以,选项A,听起来更好。但是,选项 A 的先决条件是您知道将从inputStream. 如果它是一个文件,那很简单,只需询问它的大小。如果它是流式数据,则需要在“上传代码方面”首先将整个内容流式传输到文件中,然后才将其通过网络流式传输,这很笨拙且可能效率低下。

如果你知道大小,它看起来会像(我将在这里使用更新的 API,你使用的是一些过时的、有 20 年历史的过时的东西):

// upload side
void upload() {
  Path p = Paths.get("/path/to/file/you/want/to/send");
  long fileSize = Files.size(p);
  out.write(longToBytes(fileSize);
  try (InputStream in = Files.newInputStream(p)) {
    in.transferTo(out);
  }
}

public static byte[] longToBytes(long x) {
    ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
    buffer.putLong(x);
    return buffer.array();
}

此代码具有以下属性:

  • 首先它以大端顺序发送 8 个字节,这是即将到来的数据的大小。
  • 它使用新的java.nio.fileAPI。
  • 它使用transferToInputStream 中的新方法,避免了必须声明一个字节数组作为缓冲区和 while 循环的繁琐。

然后在下载端:

void download() {
  long size = bytesToLong(in.readNBytes(8));
  Path p = Paths.get("/file/to/store/data/at");
  // Generally network transfer sizes are a bit higher than 1k;
  // up to 64k in fact. Best to declare a larger buffer!
  byte[] bb = new byte[65536];
  try (OutputStream out = Files.newOutputStream(p)) {
    while (size > 0) {
      int count = in.read(bb);
      if (count == -1) throw new IOException("Unexpected end of stream");
      out.write(bb, 0, count);
      size -= count;
    }
  }
}

public static long bytesToLong(byte[] bytes) {
    ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
    buffer.put(bytes);
    buffer.flip();//need flip 
    return buffer.getLong();
}

这段代码:

  • 首先使用新readNBytes方法读取该大小。

如果你知道传入的数据有多大,你需要写一个小协议。例如:

  • 大小以 2 个字节的形式发送,按大端顺序,无符号。然后跟随许多字节,然后发送另一个大小,无穷无尽。
  • 当流完成时,发送大小为 0(因此,值为 2 个字节0),这表示文件已完成。

如果这是您需要的,我将把它作为练习让您实现上传和下载端。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章