Java 拷贝文件的 9 种方式和FileInputStream,BufferedInputStream 和 FileReader 区别

先放总结

如果是对图片、音乐、视频、压缩包等文件的拷贝,推荐使用 缓冲字节输入/输出流(BufferedOutputStream)+字节数组 (第4种)

如果是对文本文件的读取和拷贝,推荐使用 字符缓存输入/输出流(BufferedWriter) + 按行读取 (第9种)

对于特大文件,比如 1 个 G 以上的,多线程操作可以使用 NIO (第 10 种)。

一、字节流

1、FileInputStrem 按字节流 逐字节读写(速度最慢)

/**

     * FileInputStream 方式拷贝文件

     * @param srcFileName

     * @param descFileName

     * @throws IOException

扫描二维码关注公众号,回复: 10735482 查看本文章

     */

    public static void copyFile(String srcFileName, String descFileName) throws IOException {

        Long startTime = System.currentTimeMillis();

        FileInputStream fis = new FileInputStream(srcFileName);

        FileOutputStream fos = new FileOutputStream(descFileName);

        int len = 0;

        while ((len = fis.read()) != -1) {//读取

            //写入另一个文件

            fos.write(len);

        }

        Long endTime = System.currentTimeMillis();

        System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");//2937

    }

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

        String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";

        String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";

        copyFile(srcFileName, descFileName);

    }

总共耗时:2937毫秒

2、FileInputStream 构造一个缓冲数组进行读写(速度提升很多)

/**

     * FileInputStream 方式拷贝文件,添加缓存数组

     * @param srcFileName

     * @param descFileName

     * @throws IOException

     */

    public static void copyFile(String srcFileName, String descFileName) throws IOException {

        Long startTime = System.currentTimeMillis();

        FileInputStream fis = new FileInputStream(srcFileName);

        FileOutputStream fos = new FileOutputStream(descFileName);

        byte[] buffer = new byte[1024];//构造一个长度为1024的字节数组

        int len = 0;

        while ((len = fis.read(buffer)) != -1) {//读取

            //写入另一个文件

            fos.write(buffer);

        }

        Long endTime = System.currentTimeMillis();

        System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");// 6

    }

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

        String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";

        String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";

        copyFile(srcFileName, descFileName);

    }

总共耗时:6毫秒

3、利用字节缓冲区流 BufferedInputStream 和 BufferedOutputStream来直接逐字节读写(比第1个快很多,比第2个慢)

/**

     * 使用 BufferedInputStream 和 BufferedOutputStream 通过字符拷贝

     * @param srcFileName

     * @param descFileName

     * @throws IOException

     */

    public static void copyFile(String srcFileName, String descFileName) throws IOException {

        Long startTime = System.currentTimeMillis();

        //缓冲区字节输入流

        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFileName));

        //缓冲区字节输出流

        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(descFileName));

        int len = 0;

        while ((len = bis.read()) != -1) {//读取

            //写入另一个文件

            bos.write(len);

        }

        Long endTime = System.currentTimeMillis();

        System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");// 60

    }

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

        String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";

        String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";

        copyFile(srcFileName, descFileName);

    }

总共耗时:60毫秒

4、利用字节缓冲区流BufferedInputStream和BufferedOutputStream通过构造一个缓冲数组进行读写(速度最快)

/**

     * 使用 BufferedInputStream 和 BufferedOutputStream 通过一个缓冲数组 字符拷贝

     * @param srcFileName

     * @param descFileName

     * @throws IOException

     */

    public static void copyFile(String srcFileName, String descFileName) throws IOException {

        Long startTime = System.currentTimeMillis();

        //缓冲区字节输入流

        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFileName));

        //缓冲区字节输出流

        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(descFileName));

        byte[] buffer = new byte[1024];//构造一个长度为1024的字节数组

        int len = 0;

        while ((len = bis.read(buffer)) != -1) {//读取

            //写入另一个文件

            bos.write(buffer);

        }

        Long endTime = System.currentTimeMillis();

        System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");// 4

    }

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

        String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";

        String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";

        copyFile(srcFileName, descFileName);

    }

总共耗时:4 毫秒

二、字符流

5、利用字符流InputStreamWriter和 OutputStreamWriter直接按字节读取

/**

     * 使用 InputStreamWriter和 OutputStreamWriter直接按字节读取拷贝

     * @param srcFileName

     * @param descFileName

     * @throws IOException

     */

    public static void copyFile(String srcFileName, String descFileName) throws IOException {

        Long startTime = System.currentTimeMillis();

        //缓冲区字符输入流

        InputStreamReader isr = new InputStreamReader(new FileInputStream(srcFileName));

        //缓冲区字符输出流

        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(descFileName));

        int len = 0;

        while ((len = isr.read()) != -1) {//直接按字节读取

            //写入另一个文件

            osw.write(len);

        }

        Long endTime = System.currentTimeMillis();

        System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");// 586

    }

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

        String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";

        String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";

        copyFile(srcFileName, descFileName);

    }

总共耗时:586毫秒

会乱码

6、字符流InputStreamWriter和 OutputStreamWriter直接用缓冲区数组读写

/**

     * 使用 InputStreamWriter和 OutputStreamWriter直接按字节读取拷贝

     * @param srcFileName

     * @param descFileName

     * @throws IOException

     */

    public static void copyFile(String srcFileName, String descFileName) throws IOException {

        Long startTime = System.currentTimeMillis();

        //缓冲区字符输入流

        InputStreamReader isr = new InputStreamReader(new FileInputStream(srcFileName));

        //缓冲区字符输出流

        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(descFileName));

        char[] buffer = new char[1024];//构造一个长度为1024的字节数组

        int len = 0;

        while ((len = isr.read(buffer)) != -1) {//直接按字节读取

            //写入另一个文件

            osw.write(buffer);

        }

        Long endTime = System.currentTimeMillis();

        System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");// 385

    }

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

        String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";

        String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";

        copyFile(srcFileName, descFileName);

    }

总共耗时:385毫秒

7、字符缓冲流BufferedWriter和BufferedReader直接逐字节读写

/**

     * 使用 BufferedWriter和BufferedReader直接逐字节读写

     * @param srcFileName

     * @param descFileName

     * @throws IOException

     */

    public static void copyFile(String srcFileName, String descFileName) throws IOException {

        Long startTime = System.currentTimeMillis();

        //缓冲区字符输入流

        BufferedReader bf = new BufferedReader(new FileReader(srcFileName));

        //缓冲区字符输出流

        BufferedWriter bw = new BufferedWriter(new FileWriter(descFileName));

        int len = 0;

        while ((len = bf.read()) != -1) {//直接按字节读取

            //写入另一个文件

            bw.write(len);

        }

        Long endTime = System.currentTimeMillis();

        System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");// 412

    }

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

        String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";

        String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";

        copyFile(srcFileName, descFileName);

    }

总共耗时:433毫秒

8、字符缓冲流 BufferedWriter 和 BufferedReader 按照数组大小逐块读写

/**

    * 使用 BufferedWriter和BufferedReader直接逐字节读写

    * @param srcFileName

    * @param descFileName

    * @throws IOException

    */

   public static void copyFile(String srcFileName, String descFileName) throws IOException {

       Long startTime = System.currentTimeMillis();

       //缓冲区字符输入流

       BufferedReader bf = new BufferedReader(new FileReader(srcFileName));

       //缓冲区字符输出流

       BufferedWriter bw = new BufferedWriter(new FileWriter(descFileName));

       int len = 0;

       char[] buffer = new char[1024];

       while ((len = bf.read(buffer)) != -1) {//直接按字节读取

           //写入另一个文件

           bw.write(buffer);

       }

       Long endTime = System.currentTimeMillis();

       System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");// 387

   }

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

       String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";

       String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";

       copyFile(srcFileName, descFileName);

   }

总共耗时:381毫秒

9、字符缓冲流BufferedWriter和BufferedReader按逐行读写(应用于文本读写)

/**

    * 使用 BufferedWriter和BufferedReader直接逐字节读写

    * @param srcFileName

    * @param descFileName

    * @throws IOException

    */

   public static void copyFile(String srcFileName, String descFileName) throws IOException {

       Long startTime = System.currentTimeMillis();

       //缓冲区字符输入流

       BufferedReader bf = new BufferedReader(new FileReader(srcFileName));

       //缓冲区字符输出流

       BufferedWriter bw = new BufferedWriter(new FileWriter(descFileName));

       String str;

       while ((str = bf.readLine()) != null) {//直接按字节读取

           //写入另一个文件

           bw.write(str);

       }

       Long endTime = System.currentTimeMillis();

       System.out.println("总共耗时:" + (endTime - startTime) + "毫秒");// 353

   }

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

       String srcFileName = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";

       String descFileName = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";

       copyFile(srcFileName, descFileName);

   }

总共耗时:353 毫秒

注意:

后面的几种字符流的均会出现中文字符乱码的情况。

需要指定字符集

  1. InputStreamReader isr = new InputStreamReader(new FileInputStream(srcFileName),"GBK")
  2. BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(srcFileName),"GBK"));

以上只是通过一个 1MB 的文件的复制测试,并不能说明问题。

总结:

复制文本文件有几种方式?

 9种

复制图片文件有几种方式?

4种

         

他们各自的优缺点是什么?

字符流按字符处理数据,速度较之于同原理的字节流快,但是使用范围较小,仅限文本文件;

字节流处理范围广,文本,音频,视频都可以处理。

三、FileInputStream 与BufferedInputStream区别:

FileInputStream是字节流,BufferedInputStream是字节缓冲流,使用BufferedInputStream读资源比FileInputStream读取资源的效率高(BufferedInputStream的read方法会读取尽可能多的字节),且FileInputStream对象的read方法会出现阻塞;

  1. 了解“堵塞”的意思吧!
  2. 假设一个文件的长度是100个字节,要将之读取到内存中,再假设您每次只读取10个字节,那么读完整个文件是不是读取10次的呀?
  3. 假设老板让你完成100件事情,老板说,你每天只完成10件就可以了,难道你非得等到第十天才完成第100件事情吗?有一天您在中午下班前就完成了10件事情,下午您不妨多干一点,那么也许在第9天的时候就完成了100件事情
  4. 同理,BufferedInputStream有可能会读取比您规定的更多的东西到内存,以减少访问IO的次数,
  5. 总之您要记住一句话,访问IO的次数越少,性能就越高,原因就在于CPU和内存的速度》》》》远大于硬盘或其他外部设备的速度。
  6. 换一个不太恰当的例子来说,您和您的朋友一起去登山,你朋友太不给力了,走一会儿就要休息,而您呢,您的体力比他要好的多,根本不需要休息,所以每当他休息的时候,您得等着他,您那时候什么也干不了,这就叫堵塞,堵塞就是说您有能力干某事,但是迫于某种原因您什么也干不了,只能干等。所以您朋友休息的次数越少,你们两个到达山顶所花费的时间就越少。CPU访问硬盘的次数越少,程序就越快。BufferedInputStream在小型文件中的性能优势无法体现出来,假设您将以个2G大小的文件从D盘完全复制到E盘,性能之优势便展露无疑!

四、FileInputStream与FileReader区别:

两个类的构造函数的形式和参数都是相同的,参数为 File 对象或者表示路径的 String ,它们到底有何区别呢?

FileInputStream :以字节流方式读取;

FileReader :把文件转换为字符流读入;

InputStream提供的是字节流的读取,而非文本读取,这是和Reader类的根本区别。用Reader读取出来的是char数组或者String ,使用InputStream读取出来的是byte数组。

Reader类及其子类提供的字符流的读取char,inputStream及其子类提供字节流的读取byte,所以FileReader类是将文件按字符流的方式读取,FileInputStream则按字节流的方式读取文件;InputStreamReader可以将读如stream转换成字符流方式,是reader和stream之间的桥梁

最初Java是不支持对文本文件的处理的,为了弥补这个缺憾而引入了Reader和Writer两个类。

FileInputStream 类以二进制输入 / 输出, I/O 速度快且效率搞,但是它的 read ()方法读到的是一个字节,很不利于人们阅读。 而 FileReader 类弥补了这个缺陷,可以以文本格式输入/ 输出,非常方便;比如可以使用 while((ch = filereader.read())!=-1 ) 循环来读取文件;可以使用BufferedReader 的 readLine() 方法一行一行的读取文本。 当我们读写文本文件的时候,采用 Reader 是非常方便的,

比如 FileReader , InputStreamReader 和 BufferedReader 。其中最重要的类是 InputStreamReader ,它是字节转换为字符的桥梁。 你可以在构造器中指定编码的方式,如果不指定的话将采用底层操作系统的默认编码方式,例如 GBK 等。 FileReader 与 InputStreamReader 涉及编码转换 ( 指定编码方式或者采用 os 默认编码 ) ,可能在不同的平台上出现乱码现象!而 FileInputStream 以二进制方式处理,不会出现乱码现象 .

如果处理纯文本文件,建议使用 FileReader ,因为更方便,也更适合阅读;但是要注意编码问题!

其他情况(处理非纯文本文件),FileInputStream是唯一的选择;FileInputStream是进Socket通讯时会用到很多,如将文件流是Stream的方式传向服务器!

五、NIO

10、使用 NIO 复制

/**

     * NIO

     * @param sourcePath

     * @param targetPath

     * @throws IOException

     */

    public static void copyFile(String sourcePath, String targetPath) throws IOException {

        FileInputStream in = new FileInputStream(sourcePath);

        FileOutputStream out = new FileOutputStream(targetPath);

        FileChannel inChannel = in.getChannel();

        FileChannel outChannel = out.getChannel();

        ByteBuffer buffer = ByteBuffer.allocate(4 * 1024);

        long startTime = System.currentTimeMillis();

        while (inChannel.read(buffer) != -1) {

            buffer.flip();

            outChannel.write(buffer);

            buffer.clear();

        }

        long endTime = System.currentTimeMillis();

        System.out.println("总共耗时" + (endTime - startTime) + "ms");

    }

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

        String sourcePath = "/Users/liuyanzhao/Desktop/temp/三国演义.txt";

        String targetPath = "/Users/liuyanzhao/Desktop/temp/三国演义的拷贝.txt";

        copyFile(sourcePath, targetPath);//13ms

    }

IO是阻塞的。NIO是非阻塞的,在多线程的时候效果比较明显。

发布了849 篇原创文章 · 获赞 54 · 访问量 32万+

猜你喜欢

转载自blog.csdn.net/dubo_csdn/article/details/103450317