Должен ли я закрыть FileChannel? - PullRequest
12 голосов
/ 09 ноября 2011

Сегодня я столкнулся с проблемой с одним из наших служебных классов.Это помощник для файлов и содержит некоторые статические процедуры копирования файлов.Ниже приведены соответствующие методы, извлеченные вместе с методом тестирования.

Проблема в том, что иногда происходит сбой вызова setLastModified, возвращая значение false.

На моем ПК (Windows 7, последняя версия Java) я иногда получаюсообщение «setLastModified failed» (примерно 25 раз из 1000).

Я обошел проблему прямо сейчас, удалив вызовы FileChannel.close, но я бы предпочел понять, почему это происходит, даже еслиэто правильное решение.

Кто-нибудь еще получает такую ​​же проблему?

private void testCopy() throws FileNotFoundException, IOException {
  File src = new File("C:\\Public\\Test-Src.txt");
  File dst = new File("C:\\Public\\Test-Dst.txt");

  for (int i = 0; i < 1000; i++) {
    copyFile(src, dst);
  }
}

public static void copyFile(final File from, final File to) throws FileNotFoundException, IOException {
  final String tmpName = to.getAbsolutePath() + ".tmp";
  // Copy to a .tmp file.
  final File tmp = new File(tmpName);
  // Do the transfer.
  transfer(from, tmp);
  // Preserve time.
  if (!tmp.setLastModified(from.lastModified())) {
    System.err.println("setLastModified failed!");
  }
  // In case there's one there already.
  to.delete();
  // Rename it in.
  tmp.renameTo(to);
}

public static void transfer(final File from, final File to) throws IOException {
  FileInputStream in = null;
  FileOutputStream out = null;
  try {
    in = new FileInputStream(from);
    out = new FileOutputStream(to);
    transfer(in, out);
  } finally {
    if (null != in) {
      in.close();
    }
    if (null != out) {
      out.close();
    }
  }
}

public static void transfer(final FileInputStream from, final FileOutputStream to) throws IOException {
  FileChannel srcChannel = null;
  FileChannel dstChannel = null;
  //try {
    srcChannel = from.getChannel();
    dstChannel = to.getChannel();
    srcChannel.transferTo(0, srcChannel.size(), dstChannel);
  //} finally {
  //  if (null != dstChannel) {
  //    dstChannel.close();
  //  }
  //  if (null != srcChannel) {
  //    srcChannel.close();
  //  }
  }
}

Редактировать: Я изменил код, чтобы закрыть только Streams sа не FileChannel с, потому что исследования показывают, что закрытие FileChannel также закрывает Stream.

Ответы [ 2 ]

10 голосов
/ 21 ноября 2011

После некоторых исследований среди различных сайтов, которые содержат исходники java-библиотеки, он выглядит очень похоже на FileChannel.close, в конечном счете вызывающий FileInputStream.close или FileOutputStream.close своего родительского объекта.

Это подсказывает мне, что вы должны закрыть FileChannel или Stream, но не оба .

Ввиду этого я изменяю свой исходный пост, чтобы отразить один правильный метод, то есть закрыть Stream s, а не Channel s.

3 голосов
/ 12 ноября 2011

Если вы используете Java 7, вы можете использовать Files.copy (путь к источнику, путь к пути, параметры CopyOption ...) для этой операции, чтобы избежать написания, тестирования и отладки вашей собственной реализации.

В качестве альтернативы рассмотрите возможность использования внешней библиотеки, такой как Apache Commons IO .В частности, вы найдете FileUtils.copyFile (файл srcFile, файл destFile) интересно:

/** 
 * Copies a file to a new location preserving the file date.
 * [...]
 * @param srcFile  an existing file to copy, must not be <code>null</code>
 * @param destFile  the new file, must not be <code>null</code>
 * [...]
 */
public static void copyFile(File srcFile, File destFile) throws IOException
...