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

Подождите, пока файл не будет полностью написан

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

class Program
{
    static void Main(string[] args)
    {
        string path = @"D:\levan\FolderListenerTest\ListenedFolder";
        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:\levan\FolderListenerTest\CopiedFilesFolder\" + e.Name);
        Console.Read();
    }
}
4b9b3361

Ответ 1

Существует только обходной путь для проблемы, с которой вы сталкиваетесь.

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

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

Второй метод:

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

Ответ 2

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

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

Итак, если копия завершается с ошибкой (catch исключение), добавьте ее в список файлов, которые все еще нужно перенести, и попробуйте копию во время события 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 {}
         }
   }

Ответ 3

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

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

Файлы не были заблокированы.

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

Ответ 4

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

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

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

Ответ 5

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

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

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

   CopyFile()
}

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

Ответ 6

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

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

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

В этом коде используется File.Copy, но он работает точно также с любым из статических методов класса File: File.Open, 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 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;
    }
}

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

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!

Ответ 7

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

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

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

Ответ 8

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

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!

Ответ 9

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

У меня появилось окно Windows Explorer открытой папки вывода. Я закрыл его, и все работало как прелесть.

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