尽管Java提供了java.io.File这样一个操作文件的类,但并没有提供一个复制文件的方法。
然而,当我们需要对磁盘上的文件进行处理的时候,这是一个很重要的方法。在这个时候我们往往不得不自己实现这样一个完成文件复制操作的方法。下面将会介绍4种常见的文件复制的实现,并比较下它们的性能。
使用FileStream
能找到的最常见经典例子。从文件A的输入流读取一批字节,写到文件B的输出流。
public static void copy(String from, String to, int bufferSize) {
InputStream in = null;
OutputStream out = null;
try {
in = new FileInputStream(new File(from));
out = new FileOutputStream(new File(to));
byte[] buffer = new byte[bufferSize];
int len;
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
} catch (Exception e) {
Log.w(TAG + ":copy", "error occur while copy", e);
} finally {
safelyClose(TAG + ":copy", in);
safelyClose(TAG + ":copy", out);
}
}
如你所见,这种实现方式需要多次读取数据,再写入将数据写入,因此受限于我们提供的buffer的大小,他的效率有点一般。
使用FileChannel
Java NIO类库里引入了一个叫transferFrom的方法,文档里说这是一个会比FileStream方式更快的复制操作。
public static void copyNio(String from, String to) {
FileChannel input = null;
FileChannel output = null;
try {
input = new FileInputStream(new File(from)).getChannel();
output = new FileOutputStream(new File(to)).getChannel();
output.transferFrom(input, 0, input.size());
} catch (Exception e) {
Log.w(TAG + "copyNio", "error occur while copy", e);
} finally {
safelyClose(TAG + "copyNio", input);
safelyClose(TAG + "copyNio", output);
}
}
使用Apache Commons IO
Appache Commons IO 提供了一个FileUtils.copyFile(File from, File to)方法用于文件复制,如果项目里使用到了这个类库,使用这个方法是个不错的选择。它的内部也是使用Java NIO的FileChannel实现的。
private static void copyFileUsingApacheCommonsIO(File source, File dest) throws IOException {
FileUtils.copyFile(source, dest);
}
使用Java 7 的Files类
如果对Java 7 的使用有经验的话,那应该接触过Files这个工具类。
private static void copyFileUsingApacheCommonsIO(File source, File dest) throws IOException {
FileUtils.copyFile(source, dest);
}
性能
由于项目里没用到Apache Common IO,Android也不支持Java 7,因此我只测试了前两种,数据如下:
复制文件,大小2M
| buffer大小 | 耗时 |
|---|---|
| 512 | 455 |
| 1024 | 238 |
| 2048 | 131 |
| 4096 | 82 |
| 8192 | 46 |
| 16384 | 36 |
| 32768 | 30 |
| 65536 | 29 |
| 131072 | 26 |
| NIO方式 | 31 |
复制文件,大小4.5M
| buffer大小 | 耗时 |
|---|---|
| 512 | 937 |
| 1024 | 523 |
| 2048 | 315 |
| 4096 | 155 |
| 8192 | 104 |
| 16384 | 108 |
| 32768 | 83 |
| 65536 | 74 |
| 131072 | 79 |
| NIO方式 | 75 |
复制文件,大小8M
| buffer大小 | 耗时 |
|---|---|
| 512 | 1774 |
| 1024 | 942 |
| 2048 | 488 |
| 4096 | 311 |
| 8192 | 225 |
| 16384 | 169 |
| 32768 | 154 |
| 65536 | 129 |
| 131072 | 121 |
| NIO方式 | 108 |
复制文件,大小161M
| buffer大小 | 耗时 |
|---|---|
| 512 | 38561 |
| 1024 | 19994 |
| 2048 | 10747 |
| 4096 | 5500 |
| 8192 | 3857 |
| 16384 | 3327 |
| 32768 | 3201 |
| 65536 | 3288 |
| 131072 | 3281 |
| NIO方式 | 3266 |
结论
从数据上可以看出,使用FileStream的方式,复制的效率跟我们的buffer大小取值关系很大,这无疑加大了我们使用它进行文件复制的负担。
而NIO的方式则不然,无论是小文件、还是大文件,它的效率都跟我们测试FileStream的最好水平相当!
因此,把FileStream这种老旧的实现方式从项目里挪走吧,是时候用上FileChannel了。
Written with StackEdit.