Как быстро найти добавленные / удаленные файлы? - PullRequest
11 голосов
/ 26 января 2009

Я пишу небольшую программу, которая создает индекс всех файлов в моих каталогах. Он в основном перебирает каждый файл на диске и сохраняет его в доступной для поиска базе данных, так же, как в Unix. Проблема в том, что генерация индекса довольно медленная, так как у меня около миллиона файлов.

Как только я сгенерировал индекс, есть ли быстрый способ узнать, какие файлы были добавлены или удалены на диске с момента последнего запуска?

РЕДАКТИРОВАТЬ : Я не хочу отслеживать события файловой системы. Я думаю, что риск слишком высок, чтобы выйти из синхронизации, я бы предпочел иметь что-то вроде быстрого повторного сканирования, которое быстро находит, где файлы были добавлены / удалены. Может быть, с указанием даты последнего изменения каталога или чего-то еще?

Небольшой эталон

Я только что сделал небольшой тест. Запуск

dir /b /s M:\tests\  >c:\out.txt

Занимает 0,9 секунды и дает мне всю необходимую информацию. Когда я использую реализацию Java ( очень похоже на ), это занимает около 4,5 секунд. Есть идеи, как улучшить хотя бы этот метод грубой силы?

Похожие записи: Как посмотреть, изменился ли подфайл каталога

Ответы [ 10 ]

7 голосов
/ 04 февраля 2009

Можете ли вы выпрыгнуть из Java.

Вы можете просто использовать

dir /b /s /on M:\tests\  

Сортировка по имени

если вы передадите это out.txt

Затем выполните diff до последнего запуска этого файла на Java или в командном файле. Примерно так в Дос. Вам понадобится инструмент сравнения, diff в cygwin или отличный http://gnuwin32.sourceforge.net/packages/diffutils.htm

dir /b /s /on m:\tests >new.txt
diff new.txt archive.txt >diffoutput.txt
del archive.txt
ren new.txt archive.txt

Очевидно, что вы также можете использовать класс java diff, но я думаю, что нужно принять тот факт, что команда оболочки почти всегда превосходит Java при операции со списком файлов.

6 голосов
/ 26 января 2009

К сожалению, в Java нет стандартного способа прослушивания событий файловой системы. Это может прийти в java7.

На данный момент вам придется погуглить "события java файловой системы" и выбрать пользовательскую реализацию, соответствующую вашей платформе.

4 голосов
/ 04 февраля 2009

Я сделал это в своем инструменте MetaMake. Вот рецепт:

  1. Если индекс пуст, добавьте корневой каталог в индекс с отметкой времени == dir.lastModified () - 1.
  2. Найти все каталоги в индексе
  3. Сравните временную метку каталога в индексе с меткой из файловой системы. Это быстрая операция, поскольку у вас есть полный путь (не требуется сканирование всех файлов / каталогов в дереве).
  4. Если временная метка изменилась, у вас есть изменения в этом каталоге. Пересканируйте его и обновите индекс.
  5. Если на этом шаге вы обнаружите отсутствующие каталоги, удалите поддерево из индекса
  6. Если вы столкнулись с существующим каталогом, игнорируйте его (будет проверено на шаге 2)
  7. Если вы столкнулись с новым каталогом, добавьте его с отметкой времени == dir.lastModified () - 1. Убедитесь, что это рассматривается на шаге 2.

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

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

[ПРАВИТЬ] Зак упомянул, что отметок времени недостаточно. Мой ответ: просто нет другого способа сделать это. Понятие «размер» совершенно не определено для каталогов и изменений от реализации к реализации. Нет API, в котором вы можете зарегистрироваться «Я хочу получать уведомления о любых изменениях, вносимых в файловую систему». Существуют API-интерфейсы, которые работают, когда ваше приложение работает, но если оно останавливает или пропускает событие, значит, вы не синхронизированы.

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

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

Единственный другой способ добиться того, чего хочет Зак, - это создать новую файловую систему, которая постоянно хранит эту информацию где-нибудь, продавать ее Microsoft и ждать несколько лет (вероятно, 10 или более), пока все не будут ее использовать.

3 голосов
/ 26 января 2009

Один из способов ускорить процесс - просто выполнить итерацию по каталогам и проверить время последнего изменения, чтобы увидеть, изменилось ли содержимое каталога с момента вашего последнего индекса, и если они просто провели обычное сканирование каталога тогда и посмотрим, сможете ли вы найти, где все изменилось. Я не знаю, насколько это будет переносимым, но изменение иерархии распространяется в системе Linux (может зависеть от файловой системы), так что вы можете начать с корня и работать вниз, останавливаясь при попадании в каталог, который не изменилось

1 голос
/ 30 января 2009

Учитывая, что мы не хотим отслеживать события файловой системы, можем ли мы просто отслеживать (name,size,time,checksum) каждого файла? Вычисление контрольной суммы файла (или криптографического хэша, если хотите) будет узким местом. Вы можете просто вычислить его один раз во время первоначального запуска, а затем повторно вычислить его только при необходимости (например, когда файлы совпадают по трем другим атрибутам). Конечно, нам не нужно беспокоиться об этом, если мы хотим отслеживать только имена файлов, а не содержимое файла.

Вы упоминаете, что ваша реализация Java (аналогично this ) очень медленная по сравнению с "dir /s". Я думаю, что для этого есть две причины:

  1. File.listFiles() изначально медленный. Смотрите этот предыдущий вопрос " Есть ли обходной путь для низкой производительности Java при обходе огромных каталогов? ", и этот Java RFE " File.list (FilenameFilter) не эффективен для больших каталогов " для дополнительной информации. Этот недостаток, по-видимому, исправлен в ближайшем будущем NIO.2 .

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

0 голосов
/ 05 февраля 2009

Попробуйте использовать git. Программное обеспечение для управления версиями ориентировано на решение этой проблемы, и git имеет хорошую репутацию в отношении скорости; он специально разработан для быстрой работы с локальными файлами. 'git diff --name-status' даст вам то, что вы хотите, я думаю.

0 голосов
/ 04 февраля 2009

Как насчет , как это :

private static String execute( String command ) throws IOException  { 
    Process p = Runtime.getRuntime().exec( "cmd /c " + command );
    InputStream i = p.getInputStream();
    StringBuilder sb = new StringBuilder();
    for(  int c = 0 ; ( c =  i.read() ) > -1  ; ) {
        sb.append( ( char ) c );
    }
    i.close();
    return sb.toString();
}

(Существует лот возможностей для улучшения, так как эта версия читает по одному символу за раз: Вы можете выбрать лучшую версию здесь , чтобы читать поток быстрее)

И вы используете в качестве аргумента:

"dir /b /s M:\tests\"

Если это будет использоваться в работающем приложении (точнее, в качестве автономного приложения), вы можете сбрасывать со счетов время «разогрева» JVM, которое составляет примерно 1-2 секунды в зависимости от вашего оборудования.

Вы можете попытаться увидеть, как это повлияет.

0 голосов
/ 29 января 2009

Я не проверял реализацию или производительность, но у commons-io есть метод listFiles () . Возможно, стоит попробовать.

0 голосов
/ 29 января 2009

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

0 голосов
/ 26 января 2009

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

...