Java FileInputStream FileOutputStream 在运行中的区别

问题描述

谁能告诉我为什么 1. run 是错误的? (返回码为0,但写入的文件只有原来的一半。

提前致谢!

public class FilecopyFisFos {

    public static void main(String[] args) throws IOException {

        FileInputStream fis = new FileInputStream("d:/Test1/OrigFile.MP4");
        FileOutputStream fos = new FileOutputStream("d:/Test2/DestFile.mp4");

// 1. run
//        while (fis.read() != -1){
//            int len = fis.read();
//            fos.write(len);
//        }

// 2. run
//        int len;
//        while ((len = fis.read()) != -1){
//            fos.write(len);
//        }

        fis.close();
        fos.close();
    }
}

解决方法

FileInputStreamread() 方法遵循以下逻辑:

从此输入流中读取一个字节的数据。如果尚无可用输入,则此方法会阻塞。

所以将其返回的值赋值给一个变量,如:

while((len = fis.read())!= -1) 

避免刚刚从流中读取的数据字节被遗忘,因为每个 read() 调用都将分配给您的 len 变量。


相反,此代码会绕过流中每两个字节之一,因为在 read() 条件中执行的 while 从未分配给变量。因此流前进而没有读取一半的字节(分配给 len):

while (fis.read() != -1) {      // reads a byte of data (but not saved)
   int len = fis.read();        // next byte of data saved
   fos.write(len);              // possible -1 written here    
}
,

@aran 和其他人已经指出了您问题的解决方案。

然而,这有更多方面,所以我扩展了你的例子:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileCopyFisFos {

    public static void main(final String[] args) throws IOException {
        final File src = new File("d:/Test1/OrigFile.MP4");
        final File sink = new File("d:/Test2/DestFile.mp4");

        {
            final long startMS = System.currentTimeMillis();
            final long bytesCopied = copyFileSimple(src,sink);
            System.out.println("Simple copy transferred " + bytesCopied + " bytes in " + (System.currentTimeMillis() - startMS) + "ms");
        }
        {
            final long startMS = System.currentTimeMillis();
            final long bytesCopied = copyFileSimpleFaster(src,sink);
            System.out.println("Simple+Fast copy transferred " + bytesCopied + " bytes in " + (System.currentTimeMillis() - startMS) + "ms");
        }
        {
            final long startMS = System.currentTimeMillis();
            final long bytesCopied = copyFileFast(src,sink);
            System.out.println("Fast copy transferred " + bytesCopied + " bytes in " + (System.currentTimeMillis() - startMS) + "ms");
        }

        System.out.println("Test completed.");
    }

    static public long copyFileSimple(final File pSourceFile,final File pSinkFile) throws IOException {
        try (
                final FileInputStream fis = new FileInputStream(pSourceFile);
                final FileOutputStream fos = new FileOutputStream(pSinkFile);) {

            long totalBytesTransferred = 0;
            while (true) {
                final int readByte = fis.read();
                if (readByte < 0) break;

                fos.write(readByte);
                ++totalBytesTransferred;
            }
            return totalBytesTransferred;
        }
    }

    static public long copyFileSimpleFaster(final File pSourceFile,final File pSinkFile) throws IOException {
        try (
                final FileInputStream fis = new FileInputStream(pSourceFile);
                final FileOutputStream fos = new FileOutputStream(pSinkFile);
                BufferedInputStream bis = new BufferedInputStream(fis);
                BufferedOutputStream bos = new BufferedOutputStream(fos);) {

            long totalBytesTransferred = 0;
            while (true) {
                final int readByte = bis.read();
                if (readByte < 0) break;

                bos.write(readByte);
                ++totalBytesTransferred;
            }
            return totalBytesTransferred;
        }
    }

    static public long copyFileFast(final File pSourceFile,final File pSinkFile) throws IOException {
        try (
                final FileInputStream fis = new FileInputStream(pSourceFile);
                final FileOutputStream fos = new FileOutputStream(pSinkFile);) {

            long totalBytesTransferred = 0;
            final byte[] buffer = new byte[20 * 1024];
            while (true) {
                final int bytesRead = fis.read(buffer);
                if (bytesRead < 0) break;

                fos.write(buffer,bytesRead);
                totalBytesTransferred += bytesRead;
            }
            return totalBytesTransferred;
        }
    }

}

该代码附带的提示:

  • java.nio 包通常可以更快地完成这些事情,并且代码更少。
  • 复制单个字节比批量复制慢 1'000-40'000 倍。
  • 使用 try/resource/catch 是避免文件等保留/锁定资源出现问题的最佳方法。
  • 如果您解决了一些很常见的问题,我建议您将其放在您自己的实用程序类中,甚至是您自己的库中。
  • 有像 BufferedInputStream 和 BufferedOutputStream 这样的辅助类,可以极大地提高效率;请参见示例 copyFileSimpleFaster()。
  • 但与往常一样,概念的质量对实施的影响最大;参见示例 copyFileFast()。
  • 还有更高级的概念(类似于 java.nio),其中考虑了操作系统缓存行为等概念,这将进一步提升性能。

检查我的输出,或自行运行,以查看性能差异:

Simple copy transferred 1608799 bytes in 12709ms
Simple+Fast copy transferred 1608799 bytes in 51ms
Fast copy transferred 1608799 bytes in 4ms
Test completed.