FileSystemWatcher无法访问文件,因为在其他进程中使用了文件 [英] FileSystemWatcher cannot access files because used in other process

查看:68
本文介绍了FileSystemWatcher无法访问文件,因为在其他进程中使用了文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个FileSystemWatcher,每当将图像上传到文件夹A时,它将图像从文件夹A复制到文件夹B.我试图将其用作服务器PC上的Windows服务,但是在复制文件时,我的文件被锁定的一些问题.我想我找到了问题的根源,但是我没有运气来解决它.因此,当我运行Windows服务时,它总是在第一个或第二个图片上传时意外终止.我收到的错误消息是这样的:该进程无法访问文件'filepath',因为它正在被另一个进程使用.

I'm writing a FileSystemWatcher which is to copy images from folder A to folder B, whenever an image is uploaded to folder A. I'm trying to use this as a windows service on the server PC but I'm having some issues where my files are locked when they are to be copied. I think I've found the root to my issue, but I'm not having any luck solving it. So, when I run my windows service it always ends unexpectedly at either the first or the second picture upload. The error message I'm getting says this: The process cannot access the file 'filepath' because it is being used by another process.

我的代码的相关部分:

public void WatchForChanges()
{
    FileSystemWatcher watcher = new FileSystemWatcher();
    watcher.Path = Program.SourceFolder;
    watcher.Created += new FileSystemEventHandler(OnImageAdded);
    watcher.EnableRaisingEvents = true;
    watcher.IncludeSubdirectories = true;
}

public void OnImageAdded(object source, FileSystemEventArgs e)
{
    FileInfo file = new FileInfo(e.FullPath);
    ImageHandler handler = new ImageHandler();
    if (handler.IsImage(file))
    {
        handler.CopyImage(file);
    }
}

,还有我的CopyImage方法,其中包括我针对此问题提出的一种解决方案,它利用while循环捕获错误并重试图像的复制:

and, my CopyImage method, which includes one of my proposed solutions to this problem, utilizing a while loop that catches the error and retries the copying of the image:

public void CopyImage(FileSystemInfo file)
{
    // code that sets folder paths
    // code that sets folder paths

    bool retry = true;
    if (!Directory.Exists(targetFolderPath))
    {
        Directory.CreateDirectory(targetFolderPath);
    }
    while (retry)
    {
        try
        {
            File.Copy(file.FullName, targetPath, true);
            retry = false;
        }
        catch (Exception e)
        {
            Thread.Sleep(2000);
        }
    }
}

,但是此 CopyImage 解决方案只是继续复制相同的文件,对于我而言,这不是很理想.我希望这足够了,但可悲的是我要等待一排图像.

but this CopyImage solution just keeps on copying the same file, which is not very ideal in my case. I wish it was enough but sadly I've got a queue of images waiting.

推荐答案

虽然看似简单,但实际上并不令人满意.

While it seems straightforward, it's really unsatisfactorily complex.

问题是,收到通知时,正在写入文件的应用程序没有用完,因此存在并发性问题.没有什么好办法知道文件何时关闭.嗯..一种方法是订阅日记事件-FileSystemWatcher就是这样做的-但这相当复杂,并且需要移动零件的 lot .使用此路由,文件关闭时会通知您.如果您有兴趣,请参见https://msdn.microsoft.com/zh-CN/library/windows/desktop/aa363798(v=vs.85).aspx .

The problem is, the application that's writing the file isn't done with it when you get the notification...and so you have a concurrency problem. There is no great way to know when the file closes. Well..one way is to subscribe to journal events - which is what FileSystemWatcher does - but this is fairly involved and requires a lot of moving parts. Going this route, you can be notified when the file closes. If you're interested, see https://msdn.microsoft.com/en-us/library/windows/desktop/aa363798(v=vs.85).aspx.

我将工作分为两部分.我想我要启动一个 ThreadPool 线程来完成工作,并让它从 FileSystemWatcher的事件处理程序写入的列表中读取其工作.这样,事件处理程序将快速返回. ThreadPool 线程将通过它的列表,尝试在文件上获得排他锁(类似于Tommaso的代码).如果不能,则仅移至下一个文件.每次成功复制,它都会从列表中删除该文件.

I'd divide the work into two parts. I think I'd start a ThreadPool thread to do the work, and have it read it's work from a list that the FileSystemWatcher's event handler writes to. That way, the event handler returns quickly. The ThreadPool thread would go through it's list, attempting to get an exclusive lock (similar to Tommaso's code) on the file. If it can't, it just moves on to the next file. Every time it successfully copies, it removes that file from the list.

您需要关注线程安全性...因此,您需要创建一个静态对象来协调对列表的写入.编写时,事件处理程序和 ThreadPool 线程都将持有该锁.

You need to be concerned about thread safety...so you'd want to make a static object to coordinate writes to the list. Both the event handler and the ThreadPool thread would hold the lock while writing.

这是整个方法的支架:

internal sealed class Copier: IDisposable
{
  static object sync = new object();
  bool quit;
  FileSystemWatcher watcher;
  List<string> work;

  internal Copier( string pathToWatch )
  {
    work = new List<string>();
    watcher = new FileSystemWatcher();
    watcher.Path = pathToWatch;
    watcher.Create += QueueWork;
    ThreadPool.QueueUserWorkItem( TryCopy );
  }

  void Dispose()
  {
    lock( sync ) quit = true;
  }

  void QueueWork( object source, FileSystemEventArgs args )
  {
    lock ( sync )
    {
      work.Add( args.FullPath );
    }
  }

  void TryCopy( object args )
  {
    List<string> localWork;
    while( true )
    {
      lock ( sync ) 
      { 
        if ( quit ) return;  //--> we've been disposed
        localWork = new List<string>( work );
      }
      foreach( var fileName in localWork )
      {
        var locked = true;
        try
        {
          using 
          ( var throwAway = new FileStream
            ( fileName, 
              FileMode.Open,
              FileAccess.Read,
              FileShare.None
            )
          ); //--> no-op  - will throw if we can't get exclusive read        
          locked = false;
        }
        catch { }
        if (!locked )
        {
          File.Copy( fileName, ... );
          lock( sync ) work.Remove( fileName );
        }
      }
    }
  }
}

未经测试-在答案中写在这里...但是可以,或者类似的内容可以涵盖基础.

Not tested - wrote it right here in the answer...but it, or something like it will cover the bases.

这篇关于FileSystemWatcher无法访问文件,因为在其他进程中使用了文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆