Подтвердить что ты не робот

С# - Ожидание завершения операции копирования

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

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

UPDATE:

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

ОБНОВЛЕНИЕ 2:

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

4b9b3361

Ответ 1

То, что вы ищете, является типичным сценарием производитель/потребитель. Что вам нужно сделать, изложено в разделе "Продюсер/потребительская очередь" в этом страница. Это позволит вам использовать многопоточность (возможно, для фона рабочего) для копирования файлов, чтобы вы не блокировали основной поток службы от прослушивания системных событий, и вы можете выполнять там более значимые задачи - например, проверку новых файлов и обновление очереди, Итак 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 и очередь, чтобы обновить его и скопировать. Используя этот подход, вы можете использовать подходы вместо постоянного контроля за завершением копирования файлов. Важным моментом здесь является то, что ваша служба имеет только один экземпляр объекта File для фактического физического файла - просто убедитесь, что вы (1) заблокировали свою очередь при добавлении и удалении и (2) заблокировали фактический объект File при инициализации обновления.

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

Ответ 2

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
};

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

Ответ 3

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

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

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

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

Ответ 4

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



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 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);
    }
}