传统 IO 复制
这是 Java 里最常见的文件拷贝方式,效率比不上其它两种,而且在我本机上拷贝大文件会出现失败的情况。
拷贝小文件时,与其它两种方式差别不大,文件越大,差距越明显。
public static void traditionalCopy(File source, File desc) {
try (final FileInputStream inputStream = new FileInputStream(source);
final FileOutputStream outputStream = new FileOutputStream(desc)) {
byte[] length = new byte[2097152];
int read;
final long start = System.currentTimeMillis();
while ((read = inputStream.read(length)) != -1) {
outputStream.write(read);
}
System.out.println("traditionalCopy耗时:" + (System.currentTimeMillis() - start));
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
通过通道拷贝文件
我们有时需要将一个文件中的内容复制到另一个文件中去,最容易想到的做法是利用传统的 IO 将源文件中的内容读取到内存中,然后再往目标文件中写入。
现在,有了 NIO,我们可以利用更方便快捷的方式去完成复制操作。FileChannel 提供了一对数据转移方法 - transferFrom/transferTo,通过使用这两个方法,即可简化文件复制操作。
在拷贝大文件的时候,通过通道拷贝文件的效率是最高的(在我本机上测试出的结果)。
public static void copyFileByChannel(File source, File dest) {
try (FileChannel sourceChannel = new FileInputStream(source).getChannel();
FileChannel targetChannel = new FileOutputStream(dest).getChannel()) {
int length = 2097152;
final long start = System.currentTimeMillis();
while (sourceChannel.position() != sourceChannel.size()) {
if (sourceChannel.size() - sourceChannel.position() < length) {
length = (int) (sourceChannel.size() - sourceChannel.position());
}
sourceChannel.transferTo(sourceChannel.position(), length, targetChannel);
sourceChannel.position(sourceChannel.position() + length);
}
System.out.println("copyFileByChannel耗时:" + (System.currentTimeMillis() - start));
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
通过内存映射拷贝文件
内存映射这个概念源自操作系统,是指将一个文件映射到某一段虚拟内存(物理内存可能不连续)上去。我们通过对这段虚拟内存的读写即可达到对文件的读写的效果,从而可以简化对文件的操作。
Unix/Linux 操作系统内存映射的系统调用 mmap,Java 在这个系统调用的基础上,封装了 Java 的内存映射方法。
拷贝小文件时,内存映射的方式会比通道拷贝的方式更有效率,但是差距不是很明显。
public static void copyByMap(File source, File dest) {
try (FileChannel sourceChannel = new FileInputStream(source).getChannel();
FileChannel targetChannel = new FileOutputStream(dest).getChannel()) {
int length = 2097152;
final long start = System.currentTimeMillis();
while (sourceChannel.position() != sourceChannel.size()) {
if (sourceChannel.size() - sourceChannel.position() < length) {
length = (int) (sourceChannel.size() - sourceChannel.position());
}
MappedByteBuffer map = sourceChannel.map(FileChannel.MapMode.READ_ONLY, sourceChannel.position(), length);
targetChannel.write(map);
sourceChannel.position(sourceChannel.position() + length);
}
System.out.println("copyByMap耗时:" + (System.currentTimeMillis() - start));
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}