posts - 262,  comments - 221,  trackbacks - 0
          【一】Apache commons IO包之FileUtils

          IO文件操作,可以說是除了JDBC操作之外,日常最常用的功能之一了。IO的讀寫方式如何,直接影響到系統的性能。很多時候系統的性能瓶頸往往不是出現在對象層面,而是出現在底層的IO層面上。

          Apache commosn IO包在input, output包的基礎上,提供了一個高效,方便的文件類處理工具:FileUtils,其功能涵蓋了所有日常常用的IO操作,由于這個類的部分方法底層是基于Apache commons IO自己的讀寫流去實現的,所以在性能上會相對高于JDK自帶的類(具體原因可以參考這篇文章:IO與文件讀寫---使用Apache commons io包提高讀寫效率)

          根據Apache commons IO官方的說法,這個類可以提供如下功能:



          總體上來說,主要有:

           ※ 資源的創建、刪除
           ※ 資源的復制、移動
           ※ 資源的讀寫
           ※ 資源的比較
           ※ 資源的過濾
           ※ 資源的轉換

          【二】FileUtils的常用API及解析

          ①資源的創建、刪除

           ※ 目錄的創建: 
          forceMkdir(File directory),這個方法可以在父目錄不存在的情況下,連續創建多個目錄。但如果同名的目錄已經存在或者無權創建,則拋出異常

           ※ 文件的創建:
          touch(File file),這個方法用于創建一個size為0的文件,然后迅速關閉輸出流。如果文件已經存在則簡單地修改一下文件的modify time

           ※ 目錄/文件的刪除:
          void deleteDirectory(File directory),遞歸地刪除目錄及其下的所有內容。

          boolean deleteQuietly(File file),相比于JDK的delete()方法有兩個不同:首先它不需要被刪除目錄下的內容為空,其次它不會拋出任何IOException。

          void forceDelete(File file),強行刪除file對象,如果是目錄對象則遞歸刪除子目錄。如果刪除失敗則顯式地拋出IOException。

          void forceDeleteOnExit(File file),當JVM退出時,把file對象刪除。如果是目錄對象則遞歸刪除子目錄。


          ②資源的復制、移動

           ※ 復制目錄或文件
          copyDirectory(File srcDir, File destDir, FileFilter filter, boolean preserveFileDate),這個方法用于把源目錄及其下面的子目錄,文件一起拷貝到目標位置(名稱可改)。而且該方法允許在拷貝的過程中進行過濾,指定僅拷貝那些符合條件的資源。最后一個選項用來表明是否保留文件原有的創建、修改日期還是使用最新的日期。

          copyDirectoryToDirectory(File srcDir, File destDir),這個方法和上面的區別在于:首先上面的方法是拷貝到destDir的位置,而這個方法是拷貝到destDir“之下”的位置。其次上面的方法可以在拷貝的同時改名,這個方法不能,拷貝后仍然使用原來的名稱。

          copyFile(File srcFile, File destFile, boolean preserveFileDate),類似于上面的copyDirectory方法。

          copyFileToDirectory(File srcFile, File destDir, boolean preserveFileDate),類似與上面的copyDirectoryToDirectory方法。

           ※ 移動目錄或文件
          moveToDirectory(File srcFile, File destDir, boolean createDestDir),這個方法用于移動一個文件或者目錄到指定位置。

          ③資源的讀寫

          這一部分是FileUtils的精華部分。

           ※ 讀入文件
          FileUtils支持對文件以字節數組,字符串,行的方式讀入。對應方法分別是:

          byte[] readFileToByteArray(File file) 
          String readFileToString(File file)
          String readFileToString(File file, String encoding)
          List readLines(File file)
          List readLines(File file, String encoding)


          這里我們關心的是對于大文件,FileUtils是如何讀入的?看看下面的源代碼:
          /**
               * Reads the contents of a file into a String.
               * The file is always closed.
               *
               * 
          @param file  the file to read, must not be <code>null</code>
               * 
          @param encoding  the encoding to use, <code>null</code> means platform default
               * 
          @return the file contents, never <code>null</code>
               * 
          @throws IOException in case of an I/O error
               * 
          @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
               
          */

              
          public static String readFileToString(File file, String encoding) throws IOException {
                  InputStream in 
          = null;
                  
          try {
                      in 
          = openInputStream(file);
                      
          return IOUtils.toString(in, encoding);
                  }
           finally {
                      IOUtils.closeQuietly(in);
                  }

              }

          可以見到這個方法調用了IOUtils的toString方法,那么這個過程的底層細節是如何的呢?
          public static String toString(InputStream input, String encoding)
                      
          throws IOException {
                  StringWriter sw 
          = new StringWriter();
                  copy(input, sw, encoding);
                  
          return sw.toString();
              }


          public static void copy(InputStream input, Writer output, String encoding)
                      
          throws IOException {
                  
          if (encoding == null{
                      copy(input, output);
                  }
           else {
                      InputStreamReader in = new InputStreamReader(input, encoding);
                      copy(in, output);
                  }

              }


          /**
               * Copy bytes from a large (over 2GB) <code>InputStream</code> to an
               * <code>OutputStream</code>.
               * <p>
               * This method buffers the input internally, so there is no need to use a
               * <code>BufferedInputStream</code>.
               * 
               * 
          @param input  the <code>InputStream</code> to read from
               * 
          @param output  the <code>OutputStream</code> to write to
               * 
          @return the number of bytes copied
               * 
          @throws NullPointerException if the input or output is null
               * 
          @throws IOException if an I/O error occurs
               * 
          @since Commons IO 1.3
               
          */

              
          public static long copyLarge(InputStream input, OutputStream output)
                      
          throws IOException {
                  
          byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
                  long count = 0;
                  
          int n = 0;
                  
          while (-1 != (= input.read(buffer))) {
                      output.write(buffer, 
          0, n);
                      count 
          += n;
                  }

                  
          return count;
              }

          可以看到,Apache commons IO的底層是采用InputStreamReader來讀的,而且是連續不斷地在內存中構造String對象,如果有一個2G文件,那么將會在內存中構造一個2G的String對象。

           ※ 寫入文件
          void writeLines(File file, String encoding, Collection lines, String lineEnding),這個用法用于將內存中一個集合的內容持久化到本地文件,以行的方式寫入每一個集合元素。可以指定編碼和換行符。

          這個方法的適用場景類似于:將內存中的一批“客戶信息集合”導出到文件中
          public static void writeLines(File file, String encoding, Collection lines, String lineEnding) throws IOException {
                  OutputStream out 
          = null;
                  
          try {
                      out 
          = openOutputStream(file);
                      IOUtils.writeLines(lines, lineEnding, out, encoding);
                  }
           finally {
                      IOUtils.closeQuietly(out);
                  }

              }


          public static void writeLines(Collection lines, String lineEnding,
                      OutputStream output, String encoding) 
          throws IOException {
                  
          if (encoding == null{
                      writeLines(lines, lineEnding, output);
                  }
           else {
                      
          if (lines == null{
                          
          return;
                      }

                      
          if (lineEnding == null{
                          lineEnding 
          = LINE_SEPARATOR;
                      }

                      
          for (Iterator it = lines.iterator(); it.hasNext(); ) {
                          Object line 
          = it.next();
                          
          if (line != null{
                              output.write(line.toString().getBytes(encoding));
                          }

                          output.write(lineEnding.getBytes(encoding));
                      }

                  }

              }

          void writeStringToFile(File file, String data, String encoding),這個方法將字符串一次性寫入文件
          public static void write(String data, OutputStream output, String encoding)
                      
          throws IOException {
                  
          if (data != null{
                      
          if (encoding == null{
                          write(data, output);
                      }
           else {
                          output.write(data.getBytes(encoding));
                      }

                  }

              }

          ④資源的比較

           ※ 文件時間比較
          boolean isFileNewer(File file, Date/File/Long) 和boolean isFileOlder(File file, Date/File/Long)兩種方法,這兩種方法的內部都是采用文件的last modify time進行比較的。

           ※ 文件內容比較
          boolean contentEquals(File file1, File file2)采用逐字節比較的方式,在正式比較內容之前,會先比較以下項目:存在性、類型、長度、鏈接指向,最后才是比較內容
          InputStream input1 = null;
                  InputStream input2 
          = null;
                  
          try {
                      input1 
          = new FileInputStream(file1);
                      input2 
          = new FileInputStream(file2);
                      
          return IOUtils.contentEquals(input1, input2);

                  }
           finally {
                      IOUtils.closeQuietly(input1);
                      IOUtils.closeQuietly(input2);
                  }


              
          public static boolean contentEquals(InputStream input1, InputStream input2)
                      
          throws IOException {
                  
          if (!(input1 instanceof BufferedInputStream)) {
                      input1 
          = new BufferedInputStream(input1);
                  }

                  
          if (!(input2 instanceof BufferedInputStream)) {
                      input2 
          = new BufferedInputStream(input2);
                  }


                  
          int ch = input1.read();
                  
          while (-1 != ch) {
                      
          int ch2 = input2.read();
                      
          if (ch != ch2) {
                          
          return false;
                      }

                      ch 
          = input1.read();
                  }


                  
          int ch2 = input2.read();
                  
          return (ch2 == -1);
              }

          ⑤資源的過濾

          FileUtils提供了兩種類型的方法讓使用者可以輕松地過濾文件、目錄。它們分別是:

           ※ 基于Iterator形式的過濾
          iterateFiles(File directory, IOFileFilter fileFilter, IOFileFilter dirFilter),這個方法用于從指定的目錄下過濾文件。通過fileFilter我們可以指定要過濾的文件類型,通過dirFilter我們可以指定是否對子目錄進行同樣的過濾。如果為null則子目錄不參與過濾。

          iterateFiles(File directory, String[] extensions, boolean recursive),這個方法用于從指定的目錄下過濾文件。通過extensions我們可以指定要過濾的文件擴展名,通過recurisve我們可以指定是否對子目錄進行同樣的過濾。如果為false則子目錄不參與過濾。

           ※ 基于Collection形式的過濾
          listFiles(File directory, IOFileFilter fileFilter, IOFileFilter dirFilter),這個方法和基于Iterator形式的過濾相同,只是返回的值是一個集合。

          listFiles(File directory, String[] extensions, boolean recursive)
          ,這個方法和基于Iterator形式的過濾相同,只是返回的值是一個集合。

          ⑥資源的轉換


           從URL形式到File形式的轉換
          File toFile(URL url),這個轉換會首先將file://這個Prefix去掉,然后使用normalize方法對路徑進行規范。

           ※ 從File形式到URL形式的轉換
          URL[] toURLs(File[] files),這個轉換或對文件的路徑加上file://這個prefix,然后對路徑進行規范。


          -------------------------------------------------------------
          生活就像打牌,不是要抓一手好牌,而是要盡力打好一手爛牌。
          posted on 2010-03-08 15:07 Paul Lin 閱讀(6593) 評論(1)  編輯  收藏 所屬分類: J2SE


          FeedBack:
          # re: 【Java基礎專題】IO與文件讀寫---使用Apache commons IO簡化文件讀寫
          2015-09-24 10:29 | 阿斯蘭
          不錯  回復  更多評論
            
          <2015年9月>
          303112345
          6789101112
          13141516171819
          20212223242526
          27282930123
          45678910

          常用鏈接

          留言簿(21)

          隨筆分類

          隨筆檔案

          BlogJava熱點博客

          好友博客

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 潮安县| 鄂托克前旗| 天祝| 武陟县| 北辰区| 晋城| 湖南省| 即墨市| 元谋县| 五家渠市| 东光县| 广安市| 昂仁县| 亚东县| 万源市| 玛多县| 黄龙县| 隆子县| 柳江县| 泰宁县| 南开区| 历史| 萨迦县| 临泉县| 平罗县| 安乡县| 馆陶县| 泸溪县| 灯塔市| 永兴县| 锡林浩特市| 旺苍县| 榆社县| 平邑县| 永川市| 岱山县| 长乐市| 建德市| 嵊州市| 嘉祥县| 咸丰县|