我正在开发一个Java进程,它应该有效地(并递归地)将文件/目录从源位置复制到目标位置。
要做到这一点,我想:
要检查内容是否相同,我计划使用Apache Commons IO FileUtils
方法contentsEqual(...)
。为了进行复制,我计划使用Apache Commons IO FileUtils
方法copyFile(...)
。
所以,我提出的代码是(这只是文件,目录是递归处理到文件的这个方法):
private static void checkAndUpdateFile(File src, File dest) throws IOException {
FileOutputStream out = new FileOutputStream(dest);
FileChannel channel = out.getChannel();
FileLock lock = channel.lock();
if (!dest.exists()) {
FileUtils.copyFile(src, out);
} else if (!FileUtils.contentEquals(src, dest)) {
FileUtils.copyFile(src, out);
}
lock.release();
channel.close();
out.close();
}
这会锁定文件(很棒),并复制文件(超级)。
但是,无论何时复制文件,它都会将复制文件的上次修改时间戳设置为复制时间。这意味着对FileUtils.contentEquals(src, dest)
的后续调用将继续返回false
,因此会重新复制文件。
我真正喜欢的是类似于保留文件时间戳的FileUtils.copyFile(src, dest, true)
- 并且通过调用FileUtils.contentEquals(src, dest)
来实现。这将要求锁定在File
而不是FileOutputStream
,否则对FileUtils.copyFile(src, dest, true)
的调用将失败并因为文件被锁定而抛出异常。
或者,我考虑过FileUtils.copyFile(src, dest, true)
方法执行的操作,即调用dest.setLastModified(src.lastModified())
。但是,在释放锁之后必须调用此方法,如果同时执行同一个进程多次,则可能会导致问题。
我还考虑了将锁定放在源文件上的想法,但这没有用,因为我必须把它放在FileInputStream
上,我想传递一个{{1} } File
。
所以:
FileUtils.copyFile(src, dest)
)答案 0 :(得分:0)
您可以使用Guava Google Core Library
来比较两个文件。
ByteSource inByte = Resources.asByteSource(srcFileURL);
ByteSource outByte = Files.asByteSource(srcFileURL2);
boolean fileEquals= inByte.contentEquals(outByte));
答案 1 :(得分:0)
所以...最后,我选择编写自己的copy()
和compare()
方法来使用已被锁定的FileChannel
个对象。以下是我提出的解决方案 - 尽管我希望其他人可以提出改进建议。通过查看apache.commons.io类FileUtils
和IOUtils
的源代码可以了解这一点。
private static final int s_eof = -1;
private static final int s_byteBuffer = 10240;
private static void checkAndUpdateFile(File src, File dest) throws IOException {
FileInputStream in = new FileInputStream(src);
FileChannel srcChannel = in.getChannel();
FileChannel destChannel = null;
FileLock destLock = null;
try {
if (!dest.exists()) {
final RandomAccessFile destFile = new RandomAccessFile(dest, "rw");
destChannel = destFile.getChannel();
destLock = destChannel.lock();
copyFileChannels(srcChannel, destChannel);
dest.setLastModified(src.lastModified());
} else {
final RandomAccessFile destFile = new RandomAccessFile(dest, "rw");
destChannel = destFile.getChannel();
destLock = destChannel.lock();
if (!compareFileChannels(srcChannel, destChannel)) {
copyFileChannels(srcChannel, destChannel);
dest.setLastModified(src.lastModified());
}
}
} finally {
if (destLock != null) {
destLock.release();
}
if (destChannel != null) {
destChannel.close();
}
srcChannel.close();
in.close();
}
}
protected static void copyFileChannels(FileChannel src,
FileChannel dest) throws IOException {
final long size = src.size();
for (long pos = 0; pos < size; ) {
long count =
((size - pos) > s_byteBuffer) ? s_byteBuffer : (size - pos);
pos += dest.transferFrom(src, pos, count);
}
}
protected static boolean compareFileChannels(FileChannel a,
FileChannel b) throws IOException {
if (a.size() != b.size()) {
return false;
} else {
final ByteBuffer aBuffer = ByteBuffer.allocate(s_byteBuffer);
final ByteBuffer bBuffer = ByteBuffer.allocate(s_byteBuffer);
for (int aCh = a.read(aBuffer); s_eof != aCh; ) {
int bCh = b.read(bBuffer);
if (aCh != bCh || aBuffer.compareTo(bBuffer) != 0) {
return false;
}
aBuffer.clear();
aCh = a.read(aBuffer);
bBuffer.clear();
}
return s_eof == b.read(bBuffer);
}
}