Как обрабатывать неполные файлы? Получение исключения

Мне нужно создать программу Java, которая создаст поток для поиска файла в определенной папке (исходной папке) и немедленно выберет файл для обработки (преобразует его в формат файла csv), как только он найдет файл в исходной папке. Проблема, с которой я столкнулся сейчас, заключается в том, что файл, который поступает в исходную папку, имеет большой размер (инструмент FTP используется для копирования файла с сервера в исходную папку), поток выбирает этот файл непосредственно перед его полным копированием в исходную папку и выдает исключение. Как мне остановить поток, пока файл полностью не скопируется в исходную папку? Он должен выбрать файл для обработки только после того, как файл будет полностью скопирован в исходную папку.


person raja    schedule 04.02.2009    source источник
comment
Приятно знать, какое исключение?   -  person Adeel Ansari    schedule 04.02.2009


Ответы (7)


Самый безопасный способ — загрузить файл в другое место, а затем переместить его в целевую папку.

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

person qbeuek    schedule 04.02.2009
comment
+1 Это не самый безопасный способ, но единственный способ сделать это. Все остальные варианты либо тратят ресурсы, либо небезопасны :) - person Aaron Digulla; 04.02.2009

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

Это решение отлично работает для меня, поскольку у меня точно такой же сценарий, с которым вы столкнулись.

person Bhushan Bhangale    schedule 04.02.2009

Вы можете попробовать разные вещи:

  • Неоднократно проверяйте дату последней модификации и размер файла до тех пор, пока он не перестанет меняться в течение заданного периода времени, а затем обработайте его. (Как указывает qbeuek, это небезопасно и не детерминировано. )
  • Обрабатывать только файлы с именами, соответствующими определенным критериям (например, *.dat). Измените процесс загрузки/выгрузки FTP, чтобы загрузить/выгрузить файлы с другим именем (например, *.dat.temp) и переименовать файлы после их завершения.
  • Загрузите файлы в другое место и переместите их в каталог обработки после их завершения.
  • Как сказал Уксус, если это не сработает в первый раз, попробуйте позже. :)
person Bombe    schedule 04.02.2009
comment
Я не знаю, почему у нас иногда бывают минусы. Люди начинают стрелять в него повсюду. 100 репутации слишком мало, чтобы кого-то минусовать. Он должен быть выше, может быть 1000 или 2000. - person Adeel Ansari; 04.02.2009
comment
Что ж, иногда я голосую за вещи просто для того, чтобы сбалансировать голоса против, давая голосующим +8 баллов, которые они иначе не получили бы, так что это что-то. В этом случае я бы все равно проголосовал за ваш ответ, жестко. - person itsadok; 04.02.2009
comment
я проголосовал против вас, потому что первый пункт в вашем списке предложений не является ни безопасным, ни детерминированным. - person qbeuek; 04.02.2009
comment
Я никогда не утверждал, что это так, но вы правы. Наверное, его не должно быть в списке. - person Bombe; 04.02.2009
comment
Не могли бы вы уточнить, как только они будут завершены... как узнать, что файл был полностью перенесен в другую папку, или произойдет сбой копирования/переименования во время его выполнения? - person Stroboskop; 18.02.2009
comment
@Stroboskop, за это отвечает инструмент FTP. Когда передача завершена, инструмент, который обрабатывает данные, уведомляется (любым способом). - person Bombe; 18.02.2009

Если у вас есть некоторый контроль над процессом, который выполняет FTP, вы потенциально можете создать «файл флага» в исходном каталоге сразу ПОСЛЕ завершения ftp для большого файла.

Затем ваш поток Java должен проверить наличие этого файла флага, если он присутствует, то в исходном каталоге есть файл, готовый к обработке. Перед обработкой большого файла поток должен удалить файл флага.

Файл флага может быть любым (даже пустым файлом).

person jfpoilpret    schedule 04.02.2009

Предполагая, что у вас нет контроля над процессом FTP...

Пусть будет так. Когда вы получите исключение, попробуйте снова обработать его в следующий раз. Повторяйте это до тех пор, пока файл не будет обработан. Хорошо иметь несколько атрибутов на случай исключения, чтобы проверить их позже, например; имя, последнее изменение, размер.

Проверьте точное исключение, прежде чем принять решение об его обработке позже, исключение может возникнуть по какой-то другой причине.

person Adeel Ansari    schedule 04.02.2009

Если ваша ОС — 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");
    }
person yanjost    schedule 04.02.2009

Это в 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)

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

person user1313900    schedule 08.10.2012