Подождите, Пока Файл Не Будет Полностью Записан


при создании файла (FileSystemWatcher_Created) в одном каталоге я копирую его в другой. Но когда я создаю большой (>10 МБ) файл, он не может скопировать файл, потому что он начинает копировать уже, когда файл еще не завершен...
Это вызывает не удается скопировать файл, т. к. он используется другим процессом должен быть поднят. ; (
Какая-нибудь помощь?

class Program
{
    static void Main(string[] args)
    {
        string path = @"D:levanFolderListenerTestListenedFolder";
        FileSystemWatcher listener; 
        listener = new FileSystemWatcher(path);
        listener.Created += new FileSystemEventHandler(listener_Created);
        listener.EnableRaisingEvents = true;

        while (Console.ReadLine() != "exit") ;
    }

    public static void listener_Created(object sender, FileSystemEventArgs e)
    {
        Console.WriteLine
                (
                    "File Created:n"
                   + "ChangeType: " + e.ChangeType
                   + "nName: " + e.Name
                   + "nFullPath: " + e.FullPath
                );
        File.Copy(e.FullPath, @"D:levanFolderListenerTestCopiedFilesFolder" + e.Name);
        Console.Read();
    }
}
9 51

9 ответов:

есть только временное решение проблемы.

проверьте, является ли идентификатор файла в процессе перед началом процесса копирования. Вы можете вызвать следующую функцию, пока не получите ложное значение.

1-й метод, скопированный непосредственно из ответ:

private bool IsFileLocked(FileInfo file)
{
    FileStream stream = null;

    try
    {
        stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
    }
    catch (IOException)
    {
        //the file is unavailable because it is:
        //still being written to
        //or being processed by another thread
        //or does not exist (has already been processed)
        return true;
    }
    finally
    {
        if (stream != null)
            stream.Close();
    }

    //file is not locked
    return false;
}

2-й способ:

const int ERROR_SHARING_VIOLATION = 32;
const int ERROR_LOCK_VIOLATION = 33;
private bool IsFileLocked(string file)
{
    //check that problem is not in destination file
    if (File.Exists(file) == true)
    {
        FileStream stream = null;
        try
        {
            stream = File.Open(file, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
        }
        catch (Exception ex2)
        {
            //_log.WriteLog(ex2, "Error in checking whether file is locked " + file);
            int errorCode = Marshal.GetHRForException(ex2) & ((1 << 16) - 1);
            if ((ex2 is IOException) && (errorCode == ERROR_SHARING_VIOLATION || errorCode == ERROR_LOCK_VIOLATION))
            {
                return true;
            }
        }
        finally
        {
            if (stream != null)
                stream.Close();
        }
    }
    return false;
}

из документации FileSystemWatcher:

The OnCreated событие вызывается как только создается файл. Если файл копируется или переносится в наблюдаемый каталог, OnCreated событие будет поднято немедленно, а затем один или несколько OnChanged событий.

Итак, если копия не удается (поймать исключение), добавьте его в список файлов, которые все еще нужно переместить, и попробуйте скопировать во время OnChanged событие. В конце концов, он должен работа.

что-то вроде (неполное; конкретные перехвата исключений, инициализировать переменные, и т. д.):

    public static void listener_Created(object sender, FileSystemEventArgs e)
    {
        Console.WriteLine
                (
                    "File Created:\n"
                   + "ChangeType: " + e.ChangeType
                   + "\nName: " + e.Name
                   + "\nFullPath: " + e.FullPath
                );
        try {
            File.Copy(e.FullPath, @"D:\levani\FolderListenerTest\CopiedFilesFolder\" + e.Name);
        }
        catch {
            _waitingForClose.Add(e.FullPath);
        }
        Console.Read();
    }

    public static void listener_Changed(object sender, FileSystemEventArgs e)
    {
         if (_waitingForClose.Contains(e.FullPath))
         {
              try {
                  File.Copy(...);
                  _waitingForClose.Remove(e.FullPath);
              }
              catch {}
         }
   }

Это старый нить, но я добавлю некоторую информацию для других людей.

Я испытал аналогичную проблему с программой, которая пишет PDF-файлы, иногда они занимают 30 секунд для визуализации.. это тот же период, что мой watcher_FileCreated класс ждет перед копированием файла.

файлы не были заперты.

в этом случае я проверил размер PDF, а затем подождал 2 секунды, прежде чем сравнивать новый размер, если они были неравными, поток будет спать в течение 30 секунд и повторите попытку.

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

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

когда это "в другой раз"? Я не помню, когда FileSystemWatcher отправляет несколько событий на файл-проверьте это, вам может быть достаточно просто игнорировать событие и ждать другого. Если нет, вы всегда можете настроить время и перепроверить файл в 5 секунд.

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

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

public static void listener_Created()
{
   while CheckFileInUse()
      wait 1000 milliseconds

   CopyFile()
}

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

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

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

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

код здесь используется Файл.Копировать, но он работает так же хорошо с любым из статических методов класса File: File.открытый файл.ReadAllText, File.WriteAllText и др.

/// <param name="timeout">how long to keep trying in milliseconds</param>
static void safeCopy(string src, string dst, int timeout)
{
    while (timeout > 0)
    {
        try
        {
            File.Copy(src, dst);

            //don't forget to either return from the function or break out fo the while loop
            break;
        }
        catch (IOException)
        {
            //you could do the sleep in here, but its probably a good idea to exit the error handler as soon as possible
        }
        Thread.Sleep(100);

        //if its a very long wait this will acumulate very small errors. 
        //For most things it's probably fine, but if you need precision over a long time span, consider
        //   using some sort of timer or DateTime.Now as a better alternative
        timeout -= 100;
    }
}

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

public class FileEx
{
    public static async void CopyWaitAsync(string src, string dst, int timeout, Action doWhenDone)
    {
        while (timeout > 0)
        {
            try
            {
                File.Copy(src, dst);
                doWhenDone();
                break;
            }
            catch (IOException) { }

            await Task.Delay(100);
            timeout -= 100;
        }
    }

    public static async Task<string> ReadAllTextWaitAsync(string filePath, int timeout)
    {
        while (timeout > 0)
        {
            try {
                return File.ReadAllText(filePath);
            }
            catch (IOException) { }

            await Task.Delay(100);
            timeout -= 100;
        }
        return "";
    }

    public static async void WriteAllTextWaitAsync(string filePath, string contents, int timeout)
    {
        while (timeout > 0)
        {
            try
            {
                File.WriteAllText(filePath, contents);
                return;
            }
            catch (IOException) { }

            await Task.Delay(100);
            timeout -= 100;
        }
    }
}

и вот как он может быть использован:

public static void Main()
{
    test_FileEx();
    Console.WriteLine("Me First!");
}    

public static async void test_FileEx()
{
    await Task.Delay(1);

    //you can do this, but it gives a compiler warning because it can potentially return immediately without finishing the copy
    //As a side note, if the file is not locked this will not return until the copy operation completes. Async functions run synchronously
    //until the first 'await'. See the documentation for async: https://msdn.microsoft.com/en-us/library/hh156513.aspx
    CopyWaitAsync("file1.txt", "file1.bat", 1000);

    //this is the normal way of using this kind of async function. Execution of the following lines will always occur AFTER the copy finishes
    await CopyWaitAsync("file1.txt", "file1.readme", 1000);
    Console.WriteLine("file1.txt copied to file1.readme");

    //The following line doesn't cause a compiler error, but it doesn't make any sense either.
    ReadAllTextWaitAsync("file1.readme", 1000);

    //To get the return value of the function, you have to use this function with the await keyword
    string text = await ReadAllTextWaitAsync("file1.readme", 1000);
    Console.WriteLine("file1.readme says: " + text);
}

//Output:
//Me First!
//file1.txt copied to file1.readme
//file1.readme says: Text to be duplicated!

вы можете использовать следующий код, чтобы проверить, можно ли открыть файл с монопольным доступом (то есть он не открывается другим приложением). Если файл не закрыт, вы можете подождать несколько минут и снова проверить, пока файл не будет закрыт, и вы можете безопасно скопировать его.

вы все равно должны проверить, если файл.Копирование не удается, потому что другое приложение может открыть файл между моментом проверки файла и моментом его копирования.

public static bool IsFileClosed(string filename)
{
    try
    {
        using (var inputStream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.None))
        {
            return true;
        }
    }
    catch (IOException)
    {
        return false;
    }
}

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

long fileSize = 0;
currentFile = new FileInfo(path);

while (fileSize < currentFile.Length)//check size is stable or increased
{
  fileSize = currentFile.Length;//get current size
  System.Threading.Thread.Sleep(500);//wait a moment for processing copy
  currentFile.Refresh();//refresh length value
}

//Now file is ready for any process!

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

У меня было открыто окно Проводника Windows выходной папки. Я закрыл ее, и все сработало как по волшебству.

Я надеюсь, что это кому-то поможет.