2012-04-05 2 views
22

Предположим, что у вас был внешний процесс, записывающий файлы в какой-то каталог, и у вас был отдельный процесс, периодически пытающийся прочитать файлы из этого каталога. Проблема, которую следует избегать, - это прочитать файл, который в настоящий момент находится в процессе написания другого процесса, поэтому он будет неполным. В настоящее время процесс, который читает, использует проверку таймера минимального количества файлов, поэтому он игнорирует все файлы, если их последняя измененная дата не превышает XX секунд.Как проверить, является ли файл «полным» (полностью написанным) с Java

Мне интересно, есть ли более чистый способ решить эту проблему. Если тип файла неизвестен (может быть несколько разных форматов), существует ли какой-то надежный способ проверить заголовок файла для количества байтов, которые должны быть в файле, а также количества байтов, находящихся в данный момент в файле, чтобы подтвердить их соответствие?

Спасибо за любые мысли или идеи!

+1

У вас есть какой-либо контроль над процессом записи файлов в каталог, который вы находитесь смотреть? –

+0

Помимо переименования файла по завершении, я должен сделать так, чтобы он читал файл, поскольку он записывается (думаю, 'tail' в Unix) –

ответ

9

Вы можете использовать внешний файл маркера. Процесс записи может создать файл XYZ.lock, прежде чем он начнет создавать файл XYZ, и удалит XYZ.lock после завершения XYZ. Тогда читатель легко узнает, что он может считать файл полным, только если соответствующий .lock-файл отсутствует.

+0

Hi Michal, как мы можем проверить, что файл заблокирован "через программу. –

+0

Здесь нет никаких дополнительных блокировок в файле - тот факт, что файл существует или нет, является тем, что составляет блокировку. –

+1

Что делать, если у вас нет контроля над процессом записи? – Matthieu

2

Даже количество байтов равно, содержимое файла может быть другим.

Так что, я думаю, вам нужно сопоставить старый и новый байты файлов байтом.

1

2 варианта, которые, кажется, чтобы решить эту проблему:

  1. лучший опцию- процесс писатель уведомляет процесс чтения как-то, что написание было завершено.
  2. напишите файл на {id} .tmp, после завершения - переименуйте его в {id} .java, и процесс чтения выполняется только в * .java-файлах. переименование занимает гораздо меньше времени, и вероятность того, что этот 2-й процесс будет работать вместе, уменьшится.
1

Во-первых, есть Why doesn't OS X lock files like windows does when copying to a Samba share?, но это вариация того, что вы уже делаете.

Что касается чтения произвольных файлов и поиска размеров, некоторые файлы имеют эту информацию, некоторые - нет, но даже те, у которых нет, не имеют общего способа представления. Вам понадобится конкретная информация каждого формата и каждый из них будет управлять самостоятельно.

Если вы абсолютно должны воздействовать на файл «мгновенным», это будет сделано, тогда ваш процесс записи должен будет отправить какое-то уведомление. В противном случае вы почти зацикливаетесь на опросе файлов, и чтение каталога довольно дешево с точки зрения ввода-вывода по сравнению с чтением случайных блоков из случайных файлов.

8

То, как я это делал в прошлом, заключается в том, что процесс записи файла записывается в файл «temp», а затем перемещает файл в место для чтения, когда он закончил запись файла.

Таким образом, процесс записи будет записываться в info.txt.tmp. Когда он будет завершен, он переименует файл в info.txt. Процесс чтения тогда просто должен был проверить наличие info.txt - и он знает, что если он существует, он был написан полностью.

В качестве альтернативы вы можете написать процесс записи .txt в другой каталог, а затем переместите его в каталог чтения, если вам не нравится использование странных расширений файлов.

2

Одно простое решение, которое я уже использовал в прошлом для этого сценария с Windows, чтобы использовать boolean File.renameTo(File) и попытаться переместить исходный файл в отдельную папку размещения:

Если success является false, то potentiallyIncompleteFile все еще записывается.

2

У меня не было возможности использовать временные отметки и т. Д., Так как файлы загружаются клиентами через парную партию SFTP. они могут быть очень большими по размеру.

Его довольно хакки, но я сравниваю размер файла до и после сна несколько секунд.

Его явно не идеально, чтобы зафиксировать нить, но в нашем случае это просто работает как фон системных процессов, поэтому кажется, что они работают хорошо

private boolean isCompletelyWritten(File file) throws InterruptedException{ 
    Long fileSizeBefore = file.length(); 
    Thread.sleep(3000); 
    Long fileSizeAfter = file.length(); 

    System.out.println("comparing file size " + fileSizeBefore + " with " + fileSizeAfter); 

    if (fileSizeBefore.equals(fileSizeAfter)) { 
     return true; 
    } 
    return false; 
} 

Примечание: как указано ниже, это не может работать на окнах. Это было использовано в среде Linux.

+0

Ошибка только в случае сбоя сети – Skynet

+0

Этот код не будет работать, так как метаданные размера файла записываются как первый шаг в Windows. Поэтому всегда file.length() одинаково – debugger89

0

Это можно сделать, используя Apache Commons IO maven library FileUtils.copyFile(). Если вы попытаетесь скопировать файл и получить IOException, значит, файл не полностью сохранен.

Пример:

public static void copyAndDeleteFile(File file, String destinationFile) { 

    try { 
     FileUtils.copyFile(file, new File(fileDirectory)); 
    } catch (IOException e) { 
     e.printStackTrace(); 
     copyAndDeleteFile(file, fileDirectory, delayThreadPeriod); 
    } 

Или периодически проверять с каким размером задержки папки, содержащим этот файл:

FileUtils.sizeOfDirectory(folder); 
+0

Интересно, как Commons IO может отслеживать это. Таким образом, это, вероятно, ответит на исходный вопрос без сложной копии раньше. – Thomas

Смежные вопросы