Java 拷貝文件的 9 種方式和FileInputStream,BufferedInputStream 和 FileReader 區別

先放總結

如果是對圖片、音樂、視頻、壓縮包等文件的拷貝,推薦使用 緩衝字節輸入/輸出流(BufferedOutputStream)+字節數組 (第4種)

如果是對文本文件的讀取和拷貝,推薦使用 字符緩存輸入/輸出流(BufferedWriter) + 按行讀取 (第9種)

對於特大文件,比如 1 個 G 以上的,多線程操作可以使用 NIO (第 10 種)。

 

一、字節流

1、FileInputStrem 按字節流 逐字節讀寫(速度最慢)

/**

     * 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);

        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是非阻塞的,在多線程的時候效果比較明顯。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章