【一】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 != (n = 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 閱讀(6583)
評論(1) 編輯 收藏 所屬分類:
J2SE