Мне нужно создать программу Java, которая создаст поток для поиска файла в определенной папке (исходной папке) и немедленно выберет файл для обработки (преобразует его в формат файла csv), как только он найдет файл в исходной папке. Проблема, с которой я столкнулся сейчас, заключается в том, что файл, который поступает в исходную папку, имеет большой размер (инструмент FTP используется для копирования файла с сервера в исходную папку), поток выбирает этот файл непосредственно перед его полным копированием в исходную папку и выдает исключение. Как мне остановить поток, пока файл полностью не скопируется в исходную папку? Он должен выбрать файл для обработки только после того, как файл будет полностью скопирован в исходную папку.
Как обрабатывать неполные файлы? Получение исключения
Ответы (7)
Самый безопасный способ — загрузить файл в другое место, а затем переместить его в целевую папку.
Другой вариант, упомянутый Бомбой, заключается в изменении имени файла на другое расширение после загрузки и поиске только файлов с этим расширением.
Я только читаю файл, который не находится в режиме записи. Это наиболее безопасно, так как это означает, что никакой другой процесс не записывает в этот файл. Вы можете проверить, не находится ли файл в режиме записи, используя метод canWrite класса File.
Это решение отлично работает для меня, поскольку у меня точно такой же сценарий, с которым вы столкнулись.
Вы можете попробовать разные вещи:
Неоднократно проверяйте дату последней модификации и размер файла до тех пор, пока он не перестанет меняться в течение заданного периода времени, а затем обработайте его.(Как указывает qbeuek, это небезопасно и не детерминировано. )- Обрабатывать только файлы с именами, соответствующими определенным критериям (например, *.dat). Измените процесс загрузки/выгрузки FTP, чтобы загрузить/выгрузить файлы с другим именем (например, *.dat.temp) и переименовать файлы после их завершения.
- Загрузите файлы в другое место и переместите их в каталог обработки после их завершения.
- Как сказал Уксус, если это не сработает в первый раз, попробуйте позже. :)
Если у вас есть некоторый контроль над процессом, который выполняет FTP, вы потенциально можете создать «файл флага» в исходном каталоге сразу ПОСЛЕ завершения ftp для большого файла.
Затем ваш поток Java должен проверить наличие этого файла флага, если он присутствует, то в исходном каталоге есть файл, готовый к обработке. Перед обработкой большого файла поток должен удалить файл флага.
Файл флага может быть любым (даже пустым файлом).
Предполагая, что у вас нет контроля над процессом FTP...
Пусть будет так. Когда вы получите исключение, попробуйте снова обработать его в следующий раз. Повторяйте это до тех пор, пока файл не будет обработан. Хорошо иметь несколько атрибутов на случай исключения, чтобы проверить их позже, например; имя, последнее изменение, размер.
Проверьте точное исключение, прежде чем принять решение об его обработке позже, исключение может возникнуть по какой-то другой причине.
Если ваша ОС — Linux, а ядро > 2.6.13, вы можете использовать API уведомления о событиях файловой системы с именем inotify а>. Здесь есть реализация Java: https://bitbucket.org/nbargnesi/inotify-java.
Вот пример кода (в значительной степени вдохновленный веб-сайтом).
try {
Inotify i = new Inotify();
InotifyEventListener e = new InotifyEventListener() {
@Override
public void filesystemEventOccurred(InotifyEvent e) {
System.out.println("inotify event occurred!");
}
@Override
public void queueFull(EventQueueFull e) {
System.out.println("inotify event queue: " + e.getSource() +
" is full!");
}
};
i.addInotifyEventListener(e);
i.addWatch(System.getProperty("user.home"), Constants.IN_CLOSE_WRITE);
} catch (UnsatisfiedLinkError e) {
System.err.println("unsatisfied link error");
} catch (UserLimitException e) {
System.err.println("user limit exception");
} catch (SystemLimitException e) {
System.err.println("system limit exception");
} catch (InsufficientKernelMemoryException e) {
System.err.println("insufficient kernel memory exception");
}
Это в Grails, и я использую FileUtils библиотеку из знаменитого Apache Commons. Функция sizeof возвращает размер в байтах.
def fileModified = sourceFile.lastModified()
def fileSize = FileUtils.sizeOf(sourceFile)
Thread.sleep(3000) //sleep to calculate size difference if the file is currently getting copied
if((fileSize != FileUtils.sizeOf(sourceFile)) && (fileModified != sourceFile.lastModified())) //the file is still getting copied to return
{
if(log.infoEnabled)
log.info("File is getting copied!")
return
}
Thread.sleep(1000) //breather for picking up file just copied.
Обратите внимание, что это также зависит от того, какую утилиту или ОС вы используете для передачи файлов. Безопаснее всего скопировать файл, который был скопирован или был скопирован в другой файл или каталог. Процесс копирования является надежным и гарантирует, что файл присутствует после процесса копирования. Тот, который я использую, взят из Commons API.
FileUtils.copyFileToDirectory(файл f, каталог D)
Если вы копируете огромный файл, который находится в процессе копирования, знайте, что это займет время, и вы можете начать это в параллельном потоке или лучше иметь отдельное приложение, предназначенное для процесса передачи.