Проблема с FileSystemWatcher и FileCopy после копирования удаляет его [дубликат]

Вы забыли фактическую часть JSON - data - это словарь и еще не закодированный JSON. Напишите это следующим образом:

import json
with open('data.json', 'w') as outfile:
    json.dump(data, outfile)

Примечание: Работает как с 3.x, так и с 2.x.

50
задан levi 17 February 2016 в 08:53
поделиться

9 ответов

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

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

1st Method, скопированный непосредственно из этого ответа :

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

2nd Method:

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;
}
35
ответ дан Peter Duniho 23 August 2018 в 02:18
поделиться

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

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

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

В этом коде используется 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'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;
    }
}

Еще одна небольшая заметка о parellelism: Это синхронный метод, который будет блокировать поток как во время ожидания, так и при работе над потоком. Это самый простой подход, но если файл остается заблокированным в течение длительного времени, ваша программа может перестать отвечать на запросы. 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!
1
ответ дан ashbygeek 23 August 2018 в 02:18
поделиться

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

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

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

   CopyFile()
}

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

2
ответ дан Community 23 August 2018 в 02:18
поделиться

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

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

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

-3
ответ дан Firkamon 23 August 2018 в 02:18
поделиться
  • 1
    так что теперь ваше приложение падает, если пользователь открыл этот каталог? – Epirocks 3 February 2016 в 10:17

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

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

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

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

6
ответ дан Johnny Grimes 23 August 2018 в 02:18
поделиться

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

Вы все равно должны проверить, не сработал ли 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;
    }
}
0
ответ дан Michael 23 August 2018 в 02:18
поделиться
  • 1
    Почему нисходящий? – Michael 1 October 2014 в 04:26
  • 2
    Что возвращает этот код, если вы сталкиваетесь с Исключением, которое не является исключением IOException? – William Daniel 12 January 2016 в 19:49
  • 3
    Если File.Open создает исключение, отличное от IOException, этот метод не возвращает значение. Ответственность вызывающего абонента заключается в проверке любого из не-IOExceptions, которые могут быть подняты: msdn.microsoft.com/en-us/library/y973b725 (v = vs.110) .aspx – Michael 13 January 2016 в 21:00

Когда файл записывается в двоичном формате (байт by byte), создайте 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!
0
ответ дан Mohsen.Sharify 23 August 2018 в 02:18
поделиться

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

Событие 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 {}
         }
   }
7
ответ дан Steve Czetty 23 August 2018 в 02:18
поделиться
  • 1
    это довольно хороший способ, но мне нужно это сразу, поэтому я включил IsFileReady, пока он работает. – levi 11 June 2012 в 15:50

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

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

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

4
ответ дан zmbq 23 August 2018 в 02:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: