-1

I am using this code to monitor creation of files in certain folder:

        _watcher = new RecoveringFileSystemWatcher(SourceFolder, "*.xml"); 

        _watcher.Created += (_, e) =>
        {
            ProcessFile(e.Name);
        };

RecoveringFileSystemWatcher is a fileSystemWatcher wrapper. It's constructor is:

    public RecoveringFileSystemWatcher (string path, string filter)
    {
        _containedFSW = new FileSystemWatcher(path, filter);
    }

The process works as expected but for some files, randomly, an exception is thrown telling that the file is used by another process.

This is the method that is launched upon file creation:

        var nfo = new FileInfo(filePath);
        if (nfo.Exists)
        {
            var archivoXml = nfo.Name;

            string archivo = String.Empty;
            try
            {
                string content = Task.Run(async () => await GetFileContent(filePath)).Result;
                if (String.IsNullOrEmpty(content))
                    return false;

                XmlDocument xml = new XmlDocument();
                xml.LoadXml(content);

                //The rest of the method
             }
         }

the method GetFileContent is this:

    private async Task<string> GetFileContent(string filePath)
    {
        string content = String.Empty;

        try
        {
            Console.Write("ONE - "); InfoLog.Save($"ONE {filePath}");
            using (StreamReader sr = new StreamReader(filePath))
            {
                Console.Write("TWO - "); InfoLog.Save($"TWO {filePath}"); 
                content = await sr.ReadToEndAsync().ConfigureAwait(false);
                Console.Write($"THREE {(sr.BaseStream == null ? "Closed" : "Opened")} - "); InfoLog.Save($"THREE {(sr.BaseStream == null ? "Closed" : "Opened")} {filePath}");
                sr.Close();
                Console.WriteLine($"FOUR {(sr.BaseStream == null ? "Closed" : "Opened")}"); InfoLog.Save($"FOUR {(sr.BaseStream == null ? "Closed" : "Opened")} {filePath}");
            }
        }
        catch (Exception ex)
        {
            InfoLog.Save($"XML file could be read -> {filePath}. See error log.");
            ErrorLog.Save(ex);
        }

        return content;
    }

Look at the log information I am writing to debug the process.

I got one case with a file called 1112186.xml.... this is recorded in the log:

18/12/2018 19:12:10 ONE D:\GestorDocumental\Origen\1112186.xml
18/12/2018 19:12:10 XML file could not be read -> D:\GestorDocumental\Origen\1112186.xml. See error log.

As you see, the exception is thrown at the "using" instruction.

If I see the full log, I can see that file, 1112186.xml, is never used before, so the only chance is that FSW keeps the file opened. I don't know why, but it seems this is happening.

It is clear also that this process is locking the file, because when I exit the console application and then run again, the file can be processed.

Any help about this, please?

thanks Jaime

jstuardo
  • 3,901
  • 14
  • 61
  • 136
  • Jaime, I assume these are files being written externally and you are monitoring them. Most likely the file is locked for a write operation. Recommend that you add a retry mechanism to your code and filter on the exception type. It is not unusual to hit these issue in I/O code. – Bibberty Dec 18 '18 at 22:50
  • you can add a delay. Thread.Sleep and check again..see my answer, – Gauravsa Dec 18 '18 at 22:51
  • Possible duplicate of [FileSystemWatcher - is File ready to use](https://stackoverflow.com/questions/12268067/filesystemwatcher-is-file-ready-to-use) – Etienne de Martel Dec 18 '18 at 22:54
  • @PaulThomas you are right... this is a printer that scans documents and places them in that folder. The strange is that this does not happens always, but only randomly. Maybe the printer does not close the file when it writes it? is there a workaround I can do in my program? – jstuardo Dec 18 '18 at 22:57
  • Jamie, @Guaravsa has provided a suitable answer below. Recommend you go that direction and remember I/O always needs resilient code approach. Assume it will fail first time :) – Bibberty Dec 18 '18 at 22:58

1 Answers1

1

I usually use this method to check if file is locked. I got it from one of the link in stackoverflow.

  public static bool IsFileClosed(string filepath)
  {
        bool fileClosed = false;
        int retries = 20;
        const int delay = 400; // set a delay period = retries*delay milliseconds

        if (!File.Exists(filepath))
            return false;

        do
        {
            try
            {
                // Attempts to open then close the file in RW mode, denying other users to place any locks.
                FileStream fs = File.Open(filepath, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
                fs.Close();
                fileClosed = true; // success
            }
            catch (IOException) { }

            retries--;

            if (!fileClosed)
                Thread.Sleep(delay);
        }
        while (!fileClosed && retries > 0);

        return fileClosed;
    }

This is a new class called FileTimerWatcher (it will have logger injected):

    public FileTimerWatcher(ILogger logger) : base(logger)
    {
        if (timer == null)
        {
            // Create a timer with a 1.5 second interval.
            // monitor the files after 1.5 seconds.
            timer = new Timer(delay);

            // Hook up the event handler for the Elapsed event.
            timer.Elapsed += new ElapsedEventHandler(ProcessFolder);

            timer.AutoReset = true;
            timer.Enabled = true;
        }
    }

    private void ProcessFolder(object sender, ElapsedEventArgs e)
    {
        var LastChecked = DateTime.Now;

        string[] files = System.IO.Directory.GetFiles(SourceDirectory, somefilter, System.IO.SearchOption.TopDirectoryOnly);

        foreach (string file in files)
        {
            ProcessFile(file); // process file here
        }
    }
Gauravsa
  • 6,330
  • 2
  • 21
  • 30
  • But what I win with this? I need to process the file, not just to know if the file is locked. – jstuardo Dec 18 '18 at 22:54
  • process the file means? – Gauravsa Dec 18 '18 at 22:56
  • is the file being monitored and then moved from one directory to another? – Gauravsa Dec 18 '18 at 22:58
  • 1
    I understand your point.... your code is to be called before the actual process of the new file to assure it was closed, right? However, as I have realized, the file never gets close.... but only when i exit my console application and then open again. – jstuardo Dec 18 '18 at 23:04
  • got it. then filesystemwatcher may not be appropriate for u as u do need some delay after file gets scanned in to a folder. – Gauravsa Dec 18 '18 at 23:05
  • you can create a "filetimewatcher" which will run your filemonitoring method after a certain interval. – Gauravsa Dec 18 '18 at 23:06
  • I have used your IsFileClosed method before the actual reading and it is working... I have logged each iteration and I saw that for several files, at least one more iteration was needed to wait for the file. I will continue monitoring this execution.... if it fails at some time, I will do the file timer watcher, but apparently this will not be needed. It makes sense now that since the printer is scanning documents and writing directly to the watching folder, I need to wait until printer finishes scanning and actually closes the file. Thanks. – jstuardo Dec 18 '18 at 23:22