2011-01-13 3 views
25

У меня есть программа, которая работает как служба Windows; он обрабатывает файлы в определенной папке. Поскольку это сервис, он постоянно отслеживает папку для новых файлов, которые были добавлены. Часть задания программы состоит в том, чтобы выполнять сравнение файлов в целевой папке и флажках, не соответствующих файлам. То, что я хотел бы сделать, - это определить, выполняется ли операция копирования и когда она завершена, так что файл не будет преждевременно помечен, если файл соответствия еще не скопирован в целевую папку.C# - Ожидание завершения операции копирования

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

UPDATE:

Спасибо всем за ваши предложения

UPDATE 2:

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

+0

+1. Это очень хороший вопрос. Я еще не придумал подход, который не похож на хак. – David

+1

Этот вопрос схож и имеет несколько хороших ответов: http://stackoverflow.com/questions/30074/monitoring-files-how-to-know-when-a-file-is-complete – mfdoran

ответ

2

Что вы ищете, это типичный сценарий producer/consumer. Что вам нужно сделать, это описано в разделе 'Producer/consumer queue' об этом page. Это позволит вам использовать многопоточность (возможно, для фонаря) для копирования файлов, чтобы вы не блокировали основной поток службы от прослушивания системных событий &, вы можете выполнять там более значимые задачи - например, проверка новых файлов & обновление очереди , Таким образом, on main thread do check for new files на background threads perform the actual coping task. Из личного опыта (реализованы эти задачи) от этого подхода не так много прироста производительности, если вы не работаете на нескольких процессорах, но процесс очень чистый. & плавный + код логически разделен.

Короче говоря, то, что вам нужно сделать, это объект, как следующее:

public class File 
{ 
    public string FullPath {get; internal set;} 
    public bool CopyInProgress {get; set;} // property to make sure 
    // .. other properties if desired 
} 

Тогда следующий учебник публикуемую выше вопрос блокировку на объект File & Очередь, чтобы обновить его & скопировать его. Используя этот подход, вы можете использовать этот type approaches вместо постоянного контроля за завершением копирования файлов. Важным моментом здесь является то, что ваш сервис имеет только один экземпляр объекта File на фактический физический файл - просто убедитесь, что вы (1) заблокировали свою очередь при добавлении & удаление & (2) заблокировать фактический объект File при инициализации обновления ,

EDIT: Выше, где я говорю «там не слишком много прирост производительности от такого подхода, если» я не refere, если вы делаете этот подход в одном потоке по сравнению с @ Джейсон предполагает такой подход должен быть заметно быстрее из-за @ Решение Джейсона, выполняющее очень дорогостоящие операции ввода-вывода, которые в большинстве случаев потерпят неудачу. Это я не тестировал, но я совершенно уверен, так как мой подход не требует открытия операций ввода-вывода (только один раз), потока (только один раз) & закрыть файл (только один раз). Подход @Jason предлагает несколько открытых, открытых, открытых, открытых операций, которые будут все сбой за исключением последнего.

+0

Это очень то, что я хочу выполнить, мне нужно, чтобы моя программа ничего не делала с файлами/папками в исходном каталоге, пока копия находится в прогресс. – kingrichard2005

10

Yup, используйте FileSystemWatcher, но вместо наблюдения за созданным событием следите за измененным событием. После каждого триггера попытайтесь открыть файл. Что-то вроде этого:

var watcher = new FileSystemWatcher(path, filter); 
watcher.Changed += (sender, e) => { 
    FileStream file = null; 
    try { 
     Thread.Sleep(100); // hack for timing issues 
     file = File.Open(
      e.FullPath, 
      FileMode.Open, 
      FileAccess.Read, 
      FileShare.Read 
     ); 
    } 
    catch(IOException) { 
     // we couldn't open the file 
     // this is probably because the copy operation is not done 
     // just swallow the exception 
     return; 
    } 

    // now we have a handle to the file 
}; 

Речь идет о лучшем, что вы можете сделать, к сожалению. Нет никакого чистого способа узнать, что файл готов к использованию.

+0

Это в значительной степени, тоже обработал его. Для меня всегда было «Hack-ish», но +1, потому что у меня нет ничего лучше. – David

+1

@ivo s: Неэффективно? Дорогая? Какая разница? Это происходит на фоне потока и настолько невероятно маловероятно, чтобы быть узким местом. Тьфу. Кроме того, вы делаете предположение, что он контролирует процесс копирования. Обратите внимание, что это не было изначально в его вопросе. – jason

+2

Если вы чувствуете себя авантюристом, вы можете избежать накладных расходов на то, чтобы поймать исключения, используя вызов p/invoke, чтобы попытаться открыть файл вместо вызова оболочки .NET. Основной вызов WIN32 просто возвращает нулевой дескриптор для сбоев (вы должны маршировать вызов с помощью .Net SafeFileHandle для правильной обработки). Когда вызов наконец завершится, вы сможете создать объект FileStream из SafeFileHandle. Напишите свою собственную оболочку для вызова p/invoke, чтобы вернуть FileStream (или null при ошибке) и избежать необходимости try/catch. –

2

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

using(System.IO.File.Open("file", FileMode.Open,FileAccess.Read, FileShare.Read)) {} 

Другой способ проверить размер файла. Он будет меняться со временем, если файл будет скопирован.

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

1

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



void BlockingFileCopySync(FileInfo original, FileInfo copyPath) 
{ 
    bool ready = false; 

    FileSystemWatcher watcher = new FileSystemWatcher(); 
    watcher.NotifyFilter = NotifyFilters.LastWrite; 
    watcher.Path = copyPath.Directory.FullName; 
    watcher.Filter = "*" + copyPath.Extension; 
    watcher.EnableRaisingEvents = true; 

    bool fileReady = false; 
    bool firsttime = true; 
    DateTime previousLastWriteTime = new DateTime(); 

    // modify this as you think you need to... 
    int waitTimeMs = 100; 

    watcher.Changed += (sender, e) => 
    { 
     // Get the time the file was modified 
     // Check it again in 100 ms 
     // When it has gone a while without modification, it's done. 
     while (!fileReady) 
     { 
      // We need to initialize for the "first time", 
      // ie. when the file was just created. 
      // (Really, this could probably be initialized off the 
      // time of the copy now that I'm thinking of it.) 
      if (firsttime) 
      { 
       previousLastWriteTime = System.IO.File.GetLastWriteTime(copyPath.FullName); 
       firsttime = false; 
       System.Threading.Thread.Sleep(waitTimeMs); 
       continue; 
      } 

      DateTime currentLastWriteTime = System.IO.File.GetLastWriteTime(copyPath.FullName); 

      bool fileModified = (currentLastWriteTime != previousLastWriteTime); 

      if (fileModified) 
      { 
       previousLastWriteTime = currentLastWriteTime; 
       System.Threading.Thread.Sleep(waitTimeMs); 
       continue; 
      } 
      else 
      { 
       fileReady = true; 
       break; 
      } 
     } 
    }; 

    System.IO.File.Copy(original.FullName, copyPath.FullName, true); 

    // This guy here chills out until the filesystemwatcher 
    // tells him the file isn't being writen to anymore. 
    while (!fileReady) 
    { 
     System.Threading.Thread.Sleep(waitTimeMs); 
    } 
} 

+0

К сожалению, это решение не работает для меня. GetLastWriteTime не обновляется во время копирования файла. Я использую Windows 7, возможно, поведение на Windows Server отличается, но не уверен. – KyleLib

+0

@KyleLib - Неужели? Интересно, я тоже использовал Windows 7. Любопытно. Не уверен, что с этим делать. Возможно, мне придется сломать это и немного потрудиться. – trycatch

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