42

監視対象のディレクトリに複数のファイルを配置すると、FileSystemWatcher で問題が発生します。ディレクトリに配置されたらすぐにファイルを解析したい。通常、最初のファイルは正常に解析されますが、ディレクトリに 2 番目のファイルを追加すると、アクセスの問題が発生します。場合によっては、最初のファイルが解析されないこともあります。このディレクトリを実行して監視しているアプリケーションは 1 つだけです。最終的に、このプロセスは複数のマシンで実行され、共有ディレクトリを監視しますが、データがデータベースにインポートされ、主キーがないため、各ファイルを解析できるサーバーは 1 つだけです。

FileSystemWatcher コードは次のとおりです。

public void Run() {
  FileSystemWatcher watcher = new FileSystemWatcher("C:\\temp");
  watcher.NotifyFilter = NotifyFilters.FileName;
  watcher.Filter = "*.txt";

  watcher.Created += new FileSystemEventHandler(OnChanged);

  watcher.EnableRaisingEvents = true;
  System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
}

次に、ファイルを解析するメソッド:

private void OnChanged(object source, FileSystemEventArgs e) {
  string line = null;

  try {
    using (FileStream fs = new FileStream(e.FullPath, FileMode.Open, FileAccess.Read, FileShare.None)) {
      using (StreamReader sr = new StreamReader(fs)) {
        while (sr.EndOfStream == false) {
          line = sr.ReadLine();
          //parse the line and insert into the database
        }
      }
    }
  }
  catch (IOException ioe) {
    Console.WriteLine("OnChanged: Caught Exception reading file [{0}]", ioe.ToString());
  }

2番目のファイルを移動すると、引っかかります

System.IO.IOException: 別のプロセスで使用されているため、プロセスはファイル 'C:\Temp\TestFile.txt' にアクセスできません。

複数のマシンで実行されている場合、このエラーが表示されると予想されますが、現在は 1 つのサーバーでのみ実行されています。このファイルを使用する別のプロセスがあってはなりません - 私はそれらを作成し、アプリケーションの実行中にディレクトリにコピーします。

これは FileSystemWatcher を設定する適切な方法ですか? このファイルをロックしているものを確認するにはどうすればよいですか? 両方のファイルを解析しないのはなぜですか? FileStream を閉じる必要がありますか? FileShare.None オプションを保持したいのは、ファイルを解析するサーバーが 1 つだけであるためです。ファイルに最初にアクセスしたサーバーがファイルを解析します。

4

9 に答える 9

10

上にコメントを残しておきたかったのですが、まだ十分なポイントがありません。

この質問に対する最高評価の回答には、次のようなコード ブロックがあります。

using (Stream stream = System.IO.File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
    if (stream != null)
    {
        System.Diagnostics.Trace.WriteLine(string.Format("Output file {0} ready.", fileName));
        break;
    }
}

設定を使用する際の問題FileShare.ReadWriteは、基本的に「このファイルに読み書きしたいが、他の人も読み書きできる」というファイルへのアクセスを要求していることです。このアプローチは、私たちの状況では失敗しました。リモート転送を受信して​​いたプロセスは、ファイルをロックしていませんでしたが、アクティブに書き込みを行っていました。私たちのダウンストリーム コード (SharpZipLib) は、「ファイルを使用中です」という例外で失敗していましたFileShare.Read。ファイルを開いているプロセスが既にファイルに書き込んでいたため、この要求は失敗しました。

ただし、上記の応答のコードは緩すぎます。を使用FileShare.ReadWriteすることで、ファイルへのアクセスを取得することに成功しましたが (これは、受け入れ可能な共有制限を要求していたため)、ダウンストリーム呼び出しは引き続き失敗しました。

への呼び出しの共有設定は、または、およびNOTFile.Openのいずれかである必要があります。FileShare.ReadFileShare.None FileShare.ReadWrite

于 2011-09-21T21:33:24.440 に答える
4

OnChanged メソッドでファイルを開くときは、 を指定しています。ドキュメントFileShare.Noneによると、ファイルを開いている間にファイルを開こうとすると失敗します。あなた (およびあなたのウォッチャー) は読むだけなので、代わりに を使用してみてください。FileShare.Read

于 2009-05-16T13:01:01.127 に答える
2

FileSystemWatcherは、ファイルのコピーが開始されたときに 1 回、ファイルのコピーが終了したときに 2 回、1 つのファイルが作成されるたびにwatcher.Created イベントを2 回発生させます。最初のイベントを無視して、2 回目にイベントを処理するだけです。

イベント ハンドラの簡単な例:

private bool _fileCreated = false;
private void FileSystemWatcher_FileCreated(object sender, FileSystemEventArgs e)
{
    if (_fileCreated)
    {
        ReadFromFile();//just an example method call to access the new file
    }

    _fileCreated = !_fileCreated;
}
于 2012-02-01T15:17:20.743 に答える
2

簡単な解決策は、通知を受け取ったら filesystemwatcher を破棄することです。ファイルをコピーする前に、filesystemwatcher によって破棄されたイベントを受信するまで現在のスレッドを待機させます。その後、アクセスの問題なく、変更されたファイルのコピーを続行できます。私は同じ要件を持っていて、私が言ったこととまったく同じようにしました。出来た。

コード例:

public void TestWatcher()
{
    using (var fileWatcher = new FileSystemWatcher())
    {

        string path = @"C:\sv";
        string file = "pos.csv";

        fileWatcher.Path = path;
        fileWatcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.LastWrite;
        fileWatcher.Filter = file;

        System.EventHandler onDisposed = (sender,args) =>
        {
           eve.Set();
        };

        FileSystemEventHandler onFile = (sender, fileChange) =>
        {
           fileWatcher.EnableRaisingEvents = false;
           Thread t = new Thread(new ParameterizedThreadStart(CopyFile));
           t.Start(fileChange.FullPath);
           if (fileWatcher != null)
           {
               fileWatcher.Dispose();
           }
           proceed = false;
        };

        fileWatcher.Changed += onFile;
        fileWatcher.Created += onFile;
        fileWatcher.Disposed+= onDisposed;
        fileWatcher.EnableRaisingEvents = true;

        while (proceed)
        {
            if (!proceed)
            {
                break;
            }
        }
    }
}

public void CopyFile(object sourcePath)
{
    eve.WaitOne();
    var destinationFilePath = @"C:\sv\Co";
    if (!string.IsNullOrEmpty(destinationFilePath))
    {
        if (!Directory.Exists(destinationFilePath))
        {
            Directory.CreateDirectory(destinationFilePath);
        }
        destinationFilePath = Path.Combine(destinationFilePath, "pos.csv");
    }           

    File.Copy((string)sourcePath, destinationFilePath);
}
于 2011-02-11T11:07:53.893 に答える
1

あなたが望むものの良い例は、log4net の ConfigureAndWatchHandler だと思います。タイマーを使用して、ファイル ハンドラー イベントを発生させます。これは、0xA3 の投稿の while ループのよりクリーンな実装になると思います。dotPeek を使用してファイルを調べたくない方のために、OP コードに基づいたコード スニペットをここに示します。

private System.Threading.Timer _timer;    

public void Run() {
  //setup filewatcher
  _timer = new System.Threading.Timer(new TimerCallback(OnFileChange), (object) null, -1, -1);
}

private void OnFileChange(object state)
{
    try 
    {
    //handle files
    }
    catch (Exception ex) 
    {
        //log exception
        _timer.Change(500, -1);
    }
}
于 2011-06-14T17:18:59.040 に答える
0
public static BitmapSource LoadImageNoLock(string path)
{
    while (true)
    {
        try
        {
            var memStream = new MemoryStream(File.ReadAllBytes(path));
            var img = new BitmapImage();
            img.BeginInit();
            img.StreamSource = memStream;
            img.EndInit();
            return img;
            break;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}
于 2012-07-04T08:45:27.660 に答える
0

DFS内で同じ問題が発生しました。私の解決策は、各ファイルに 2 つの空の行を追加することで達成されました。次に、私のコードはファイル内の 2 つの空行を待ちます。次に、ファイルからデータ全体を確実に読み取ることができます。

于 2009-05-16T12:46:00.273 に答える