复制二进制数据

我正在尝试读取视频文件,并将其写入另一个文件,但由于某种原因,当我进行 md5sum 比较时,它们不同,并且 VLC 无法读取新文件。想法?

原始文件的
MD5 总和:b13d9acecd2dd3f869245c8e085f88c2 新文件的 MD5 总和:d41d8cd98f00b204e9800998ecf8427e

public void copyFile() throws IOException {
    BufferedInputStream in = new BufferedInputStream(new FileInputStream("/home/zevrant/tmp/test.h264"));
    BufferedOutputStream fileOutputStream = new BufferedOutputStream(new FileOutputStream(new File("/security/footage/test.h264")));
    while(in.available() > 0) {
        bytes = in.readNBytes(in.available());
        fileOutputStream.write(bytes);
        fileOutputStream.flush();
   }
}

回答

你的copyFile方法的根本问题是:

   while (in.available() > 0) {

这是一个问题,因为in.available()返回当前可以在不阻塞的情况下从输入流中读取的字节数。如果数据的消费者“赶上”了生产者,那可能是零字节,即使您还没有到达流的末尾:

  • 对于套接字,如果远程服务器或网络速度较慢,或者网络中出现“打嗝”,就会发生这种情况。

  • 对于管道,如果管道另一端的程序不能足够快地写入数据,就会发生这种情况。

  • 对于常规文件(或文件共享上的文件),可能会发生这种情况,文件系统预读无法在缓存中保留足够的数据。(文件的实际行为available()取决于操作系统和文件系统类型。做出假设是不明智的......)

如果发生这种情况,您的方法可能会错误地假设它已到达结尾,并在复制整个流之前关闭流......。

如果您要“手动”复制流,正确的编写方法如下:

public static void copyFile() throws IOException {
    try (
        InputStream in = new FileInputStream(...));
        OutputStream out = new FileOutputStream(new File(...))) {
        byte[] bytes = new byte[8192];   // Or use a larger buffer.
        int nosRead;
        while ((nosRead = in.read(bytes)) > 0) {
            out.write(bytes, 0, nosRead);
        }
    }
}

笔记:

  1. 我们available()不习惯决定阅读多少。我们读取多达 8192 字节的块。(这个数字可能更大,但使缓冲区太大也有缺点。)

  2. 无需为此使用缓冲流。我们正在做我们自己的(简单的)缓冲。

  3. 我们需要确保文件已关闭。一个有资源的尝试是要做到这一点(从Java 7起)的最佳方式。资源(inout)将自动关闭。(并且关闭会导致冲洗。)

  4. 也可以使用Channeland ByteBuffer,这可能会更快。


但是如果你将一个文件复制到一个文件中,那么 Java8Files.copy(...)会更简单,也更高效(可能是最高效的)。所有的复制都是在幕后完成的,可以合理地假设它将以最佳方式完成(对于 Java)。

在 Java 9 中,您还可以选择使用InputStream.transferTo(...)which 来利用特殊的 I/O 系统调用,当您将数据从一个“文件描述符”传输到另一个“文件描述符”时,这些调用可以减少内存到内存的复制。


以上是复制二进制数据的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>