繁体   English   中英

使用Java在文件锁中复制文件

[英]Copying files with file locks in Java

我正在开发一个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 ,并且我想将File传递给FileUtils.copyFile(src, dest)

所以:

  1. 有没有更简单的方法来实现我想要做的事情?
  2. 是否可以锁定文件,而不是文件的衍生物?
  3. 解决这个问题的最佳方法是什么?
    • 即我只需要为此部分编写自己的方法吗? (可能是copyFile(...)

您可以使用Guava Google Core Library来比较两个文件。

作为Byte Source

ByteSource文件

        ByteSource inByte = Resources.asByteSource(srcFileURL);
        ByteSource outByte = Files.asByteSource(srcFileURL2);
        boolean fileEquals= inByte.contentEquals(outByte));

所以...最后,我选择编写自己的copy()compare()方法来使用已锁定的FileChannel对象。 以下是我提出的解决方案 - 尽管我希望其他人可以提出改进建议。 这是通过查看apache.commons.io类的源代码通知FileUtilsIOUtils

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);
  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM