Сайт разработчика Александр Климова

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

Создание файлового маршрутизатора на основе шаблонов

В этой статье поговорим о создании файлового роутера File Router, который мониторит заданную папку жесткого диска и перемещает файлы в нужную папку, используя определенный шаблон.

В статье используется код на Visual Basic, а пример с исходными файлами, который вы можете скачать, написан на C#.

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

Утилита File Router действует следующим образом. Программа отслеживает заданную папку на жестком диске и перемещает появившиеся там файлы в нужные папки автоматически по заданному нами алгоритму. Приложение очень простое и легко настраивается.

Будем использовать приложение Windows Form с элементом NotifyIcon. Слежение за папкой будет проводиться при помощи объекта FileSystemWatcher. С его помощью легко отслеживать различные события, например, создание файла, переименование или изменение содержимого.

Наша задача – следить за появлением новых файлов (или переименования) в заданной папке. Когда эти файлы будут обнаружены, их имена сравниваются с заданным выражением. Каждое выражение содержит один или несколько простых шаблонов, разделенных запятыми, например, ".jpg" или "budget." Обратите внимание, что мы используем для шаблона не только расширения файлов, хотя это тоже не возбраняется.

В дополнение к обычной форме, используемой для настроек, проект также содержит такие элементы как NotifyIcon, FileSystemWatcher, FolderBrowserDialog, ToolTip и ContextMenuStrip.

Основная форма отображает настройки. Все настройки будем хранить в объекте FileScannerSettings. Поля в этом объекте определены следующим образом:


Shared SETTINGS_FILENAME As String = "FileRouter.settings"
Dim _activeScan As Boolean = False
Dim _sourcePath As String
Dim _recursiveScan As Boolean = False
Dim _mappings As Dictionary(Of String, FileMapping) =
    New Dictionary(Of String, FileMapping)()

ActiveScan определяет, нужно ли следить за заданной папкой. File Router может запускаться по требованию, если это нужно. Словарь Dictionary содержит ключевые слова и значение объекта FileMapping. Каждый FileMapping в свою очередь содержит строковое выражение и путь. Для сохранения настроек мы вызываем метод SaveSettings:


Public Shared Sub SaveSettings(ByVal fs As FileScannerSettings)
    Dim str As Stream = File.Open(SETTINGS_FILENAME, FileMode.Create)
    Dim bf As BinaryFormatter = New BinaryFormatter
    bf.Serialize(str, fs)
    str.Close()
End Sub

Мы используем упрощенный вариант сохранения без обработки различных ошибок.

Считывание настроек является обратным процессом:


Public Shared Function GetSettings() As FileScannerSettings
    Dim str As Stream = Nothing
    Try
        str = File.Open(SETTINGS_FILENAME, FileMode.Open)
        Dim bf As BinaryFormatter = New BinaryFormatter
        Dim fs As FileScannerSettings = _
            CType(bf.Deserialize(str), FileScannerSettings)
        Return fs
    Catch
        ' Most likely saved file does not exist
        Return New FileScannerSettings
    Finally
        If Not str Is Nothing Then
            str.Close()
        End If
    End Try
End Function

Здесь также используется минимум кода для обработки ошибки. Но это необходимо сделать, потому что файл может не существовать.

Наконец, объект FileScanner сканирует папку и сравнивает файлы. Метод ScanFiles просто получает список файлов из заданного пути и передает его в PerformMatch:


Public Sub ScanFiles()
    Dim so As SearchOption
    If _settings.RecursiveScan Then
        so = SearchOption.AllDirectories
    Else
        so = SearchOption.TopDirectoryOnly
    End If
    Dim files() As String =_
        Directory.GetFiles(_settings.SourcePath, "*.*", so)
    For Each file As String In files
        PerformMatch(file)
    Next
End Sub

Обратите внимание, что мы можем получать имена файлов только в заданной папке без учета вложенных папок или нужно рекурсивно пройтись по всем подпапкам. Эта опция отражена в объекте FileSystemWatcher, когда приложение находится в активном режиме. Метод PeformMatch сравнивает имена файлов с шаблоном (для удобства переведем имена в нижний регистр) и перемещает файл в нужную папку:


Public Sub PerformMatch(ByVal fileName As String)
    Dim fileNameAlone As String = Path.GetFileName(fileName)
    Dim fileNameLower As String = fileNameAlone.ToLower()
    For Each mapping As FileMapping In _settings.FileMappings.Values
        Dim expressions() As String = mapping.Expression.Split(",")
        For Each expression As String In expressions
            If fileNameLower.Contains(expression) Then
                Try
                    File.Move(fileName, _
                        Path.Combine( _
                        mapping.DestinationPath, fileNameAlone))
                    Return
                Catch ex As Exception
                    RaiseEvent ErrorEncountered(fileNameAlone, ex)
                    Continue For
                End Try
            End If
        Next
    Next
End Sub

Каждый шаблон разбит запятыми. Каждая подстрока затем оценивается методом Contains. Если совпадение найдено, то файл перемещается при помощи метода File.Move. Обратите внимание на собственную обработку ошибок. Сканер файлов может продолжать свою работу, даже если файл имеет ошибку.

Над чем поработать

  • Проверка различных ошибок. Например, не проверяются имена файлов, может лучше использовать диалоговые окна обзора
  • Выражения должны быть более гибкими. Использование регулярных выражений с различными звездочками могут упростить создание шаблонов вместо списка, разделенных запятыми
  • Список шаблонов не редактируется. Надо здесь также поработать

Скачать пример

Перевод: Василий Котов

Реклама