【一】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 阅读(6560)
评论(1) 编辑 收藏 所属分类:
J2SE