有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java复制二进制数据

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

MD5原始文件的总和:B13D9ACED2DD3F869245C8E085F88C2
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();
   }
}

共 (5) 个答案

  1. # 1 楼答案

    来自@Stephen的答案。C很好。只是普通复制的代码:

    public void copyFile() throws IOException {
        Path sourcePath = Paths.get("/home/zevrant/tmp/test.h264");
        Path targetPath = Paths.get("/security/footage/test.h264");
        Files.copy(sourcePath, targetPath);
    }
    
  2. # 2 楼答案

    问题是,您需要对InputStream使用read()方法,而不是available()方法,并检查read()是否返回-1(如example)。available()方法只返回当前可读取的数据量,而不是整个流的大小。如果底层inputstream来自网络(例如套接字),那么available()可以返回0,即使没有现成的数据

  3. # 3 楼答案

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

       while (in.available() > 0) {
    

    这是一个问题,因为in.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. 我们需要确保文件已经关闭。使用资源进行尝试是最好的方法(从Java7开始)。资源(inout)将自动关闭。(收盘时会导致冲水。)

    4. 也可以使用ChannelByteBuffer,这可能更快


    但是,如果要将一个文件复制到一个文件中,那么Java8 Files.copy(...)将更简单、更高效(可能是最高效的)。所有的复制都是在幕后进行的,因此可以合理地假设它将以最佳方式进行(对于Java)

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

  4. # 4 楼答案

    您正在使用in.available()在到达文件末尾之前,它可以返回0

    如果你想使用你的循环

    byte[] bytes = new byte[1024];
    
    int read;
    while ( (read = in.read(bytes)) >= 0) {
        out.write(bytes, 0, read);
    }
    

    当in关闭或耗尽时,它将返回-1

    不过,你可以使用一些更新的方法

    in.transferTo(out);
    

    或者直接使用Files选项

    Files.copy(Paths.get("/home/zevrant/tmp/test.h264"), out );
    
  5. # 5 楼答案

    在我看来,要以字节形式读取文件,请遵循以下选项:-

    1-你可以一个字节一个字节地读

    int nextByte;
    while ((nextByte = in.read()) != -1) {
        out.write(nextByte);
    }
    
    out.flush();
    

    2-可以声明一个具有特定大小(例如64)的字节数组来读取字节

    int nOfReadBytes;
    byte[] bytes = new byte[64];
    while ((nOfReadBytes = in.read(bytes)) != -1) {
        out.write(bytes, 0, nOfReadBytes);
    }
    
    out.flush();
    

    3-另一个简单且有保证的解决方案,您可以使用java.nio.file类来复制、创建和删除文件或目录。例:

    Path originalFile = Paths.get("/home/zevrant/tmp/test.h264");
    Path newFile = Paths.get("/security/footage/test.h264");
    Files.copy(originalFile, newFile);
    

    以下代码仅用于检查总和
    如果要检查文件的总和,可以使用DigestInputStreamMessageDigest包中的java.security类。例:

    public class Hash {
        public static String getHash(File file, String algorithm) {
            String result = "";
        
            try {
                MessageDigest digest = MessageDigest.getInstance(algorithm);
                DigestInputStream inputStream = new DigestInputStream(new FileInputStream(file), digest);
                while (inputStream.read() != -1);
                result = toHexString(digest.digest());
            } catch (Exception e) {
                System.err.println(e.getMessage());
            }
        
            return result;
        }
        
        private static String toHexString(byte[] bytes) {
            StringBuilder builder = new StringBuilder();
        
            for (byte b : bytes) {
                builder.append(String.format("%02x", b));
            }
        
            return builder.toString();
        }
        
    }