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

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

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

          根據(jù)Apache commons IO官方的說法,這個(gè)類可以提供如下功能:



          總體上來說,主要有:

           ※ 資源的創(chuàng)建、刪除
           ※ 資源的復(fù)制、移動(dòng)
           ※ 資源的讀寫
           ※ 資源的比較
           ※ 資源的過濾
           ※ 資源的轉(zhuǎn)換

          【二】FileUtils的常用API及解析

          ①資源的創(chuàng)建、刪除

           ※ 目錄的創(chuàng)建: 
          forceMkdir(File directory),這個(gè)方法可以在父目錄不存在的情況下,連續(xù)創(chuàng)建多個(gè)目錄。但如果同名的目錄已經(jīng)存在或者無權(quán)創(chuàng)建,則拋出異常

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

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

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

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

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


          ②資源的復(fù)制、移動(dòng)

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

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

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

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

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

          ③資源的讀寫

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

           ※ 讀入文件
          FileUtils支持對文件以字節(jié)數(shù)組,字符串,行的方式讀入。對應(yīng)方法分別是:

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


          這里我們關(guān)心的是對于大文件,F(xiàn)ileUtils是如何讀入的?看看下面的源代碼:
          /**
               * 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);
                  }

              }

          可以見到這個(gè)方法調(diào)用了IOUtils的toString方法,那么這個(gè)過程的底層細(xì)節(jié)是如何的呢?
          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來讀的,而且是連續(xù)不斷地在內(nèi)存中構(gòu)造String對象,如果有一個(gè)2G文件,那么將會(huì)在內(nèi)存中構(gòu)造一個(gè)2G的String對象。

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

          這個(gè)方法的適用場景類似于:將內(nèi)存中的一批“客戶信息集合”導(dǎo)出到文件中
          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),這個(gè)方法將字符串一次性寫入文件
          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));
                      }

                  }

              }

          ④資源的比較

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

           ※ 文件內(nèi)容比較
          boolean contentEquals(File file1, File file2)采用逐字節(jié)比較的方式,在正式比較內(nèi)容之前,會(huì)先比較以下項(xiàng)目:存在性、類型、長度、鏈接指向,最后才是比較內(nèi)容
          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),這個(gè)方法用于從指定的目錄下過濾文件。通過fileFilter我們可以指定要過濾的文件類型,通過dirFilter我們可以指定是否對子目錄進(jìn)行同樣的過濾。如果為null則子目錄不參與過濾。

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

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

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

          ⑥資源的轉(zhuǎn)換


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

           ※ 從File形式到URL形式的轉(zhuǎn)換
          URL[] toURLs(File[] files),這個(gè)轉(zhuǎn)換或?qū)ξ募穆窂郊由蟜ile://這個(gè)prefix,然后對路徑進(jìn)行規(guī)范。


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


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

          常用鏈接

          留言簿(21)

          隨筆分類

          隨筆檔案

          BlogJava熱點(diǎn)博客

          好友博客

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 甘孜| 巴林右旗| 南江县| 平邑县| 通城县| 舟山市| 定边县| 兴国县| 毕节市| 盘锦市| 鄢陵县| 射洪县| 紫云| 泊头市| 绥中县| 金乡县| 屏南县| 封开县| 莱芜市| 台前县| 北宁市| 游戏| 阿合奇县| 张掖市| 犍为县| 龙游县| 沙湾县| 安陆市| 明星| 桂阳县| 丰县| 壤塘县| 绥棱县| 湟源县| 绥化市| 修武县| 赫章县| 吉木萨尔县| 眉山市| 台北市| 张家川|