2017-10-10 51 views
3

私は新しいですが、folderWatchというメソッドから呼び出されたfileSystemWatcherを使用して.xmlファイルのフォルダを監視するプログラムを作成しています。 .xmlファイルには電子メールアドレスが含まれており、一度読み取った画像へのパスは電子メールで送信されます。一度にいくつかのXMLだけを追加すると、私はコードが正常に動作しますが、大量のファイルをfolderSystemWatcherにダンプしようとすると、そのすべてが処理されません。私を正しい方向に向けるのを助けてください。FileSystemWatcherがファイルを失う

private System.IO.FileSystemWatcher m_Watcher; 
public string folderMonitorPath = Properties.Settings.Default.monitorFolder; 

    public void folderWatch() 
    { 
     if(folderMonitorPath != "") 
     { 
      m_Watcher = new System.IO.FileSystemWatcher(); 
      m_Watcher.Filter = "*.xml*"; 
      m_Watcher.Path = folderMonitorPath; 
      m_Watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite 
            | NotifyFilters.FileName | NotifyFilters.DirectoryName; 
      m_Watcher.Created += new FileSystemEventHandler(OnChanged); 
      m_Watcher.EnableRaisingEvents = true; 
     } 
    } 

    public void OnChanged(object sender, FileSystemEventArgs e) 
    { 
     displayText("File Added " + e.FullPath); 
     xmlRead(e.FullPath); 
    } 

読み取りXML

public void xmlRead(string path) 
    { 

     XDocument document = XDocument.Load(path); 
     var photo_information = from r in document.Descendants("photo_information") 
           select new 
           { 
            user_data = r.Element("user_data").Value, 
            photos = r.Element("photos").Element("photo").Value, 
           }; 
     foreach (var r in photo_information) 
     { 
      if (r.user_data != "") 
      { 
       var attachmentFilename = folderMonitorPath + @"\" + r.photos; 
       displayText("new user data " + r.user_data); 
       displayText("attemting to send mail"); 
       sendemail(r.user_data, attachmentFilename); 
      } 
      else 
      { 
       displayText("no user data moving to next file"); 
      } 
     } 

はメール

public void sendemail(string email, string attachmentFilename) 
    { 
     //myTimer.Stop(); 

      MailMessage mail = new MailMessage(); 
      SmtpClient SmtpServer = new SmtpClient(smtpClient); 

      mail.From = new MailAddress(mailFrom); 
      mail.To.Add(email); 
      mail.Subject = "test"; 
      mail.Body = "text"; 

      SmtpServer.Port = smtpPort; 
     SmtpServer.Credentials = new System.Net.NetworkCredential("username", "password"); 
     SmtpServer.EnableSsl = true; 
     // SmtpServer.UseDefaultCredentials = true; 

     if (attachmentFilename != null) 
      { 
       Attachment attachment = new Attachment(attachmentFilename, MediaTypeNames.Application.Octet); 
       ContentDisposition disposition = attachment.ContentDisposition; 
       disposition.CreationDate = File.GetCreationTime(attachmentFilename); 
       disposition.ModificationDate = File.GetLastWriteTime(attachmentFilename); 
       disposition.ReadDate = File.GetLastAccessTime(attachmentFilename); 
       disposition.FileName = Path.GetFileName(attachmentFilename); 
       disposition.Size = new FileInfo(attachmentFilename).Length; 
       disposition.DispositionType = DispositionTypeNames.Attachment; 
       mail.Attachments.Add(attachment); 
      } 
     try 
     { 
      SmtpServer.Send(mail); 
      displayText("mail sent"); 
     } 
     catch (Exception ex) 
     { 
      displayText(ex.Message); 

     } 

    } 
+2

チャンスはそのための時間を彼らに欠けている - それをスレッドとあなたが得るためにErrorイベントを使用する必要が – BugFinder

+0

ファイルのキューを持っていますあなたがそれを間違っていると言っているFSW。 –

+0

FSWはエラーを起こしやすいです。何らかのファイルシステムのイベントのために、何のエラーも伝えずに、リスニングをランダムに停止します。興味があれば、私は[Observable FileSystemWatcher](http://idcomlog.codeplex.com/SourceControl/latest#IdComLog.Reactive/FileSystem.cs)を用意しています。 –

答えて

1

まず送って、FileSystemWatcherは保留中の通知を格納するために内部の限られたbufferを持っています。

システムはファイルの変更をコンポーネントに通知し、コンポーネントが作成してAPIに渡すバッファに変更を保存します。各 イベントは、ファイル名を除き、最大16バイトのメモリを使用できます。 短時間に多くの変更があった場合、バッファがオーバーフローする可能性があります。 これは、あなたが64 * 1024(64キロバイト、最大許容値)にInternalBufferSizeを設定することにより、そのバッファを増やすことができますコンポーネントがディレクトリに

を変更するトラックを失うことになります。

このバッファがどのようにクリアされるかは次の(さらに重要なことです)です。 OnChangedハンドラが呼び出され、処理が完了すると通知がそのバッファから削除されます。これは、ハンドラで多くの作業を行う場合、バッファがオーバーフローする確率が非常に高いことを意味します。これを避けるために - OnChangedハンドラの中で可能な限り小さな仕事でやると(ちょうどillustationの目的のためではなく、生産準備のコード)は、例えば、別のスレッド内のすべての重い作業を行う:

var queue = new BlockingCollection<string>(new ConcurrentQueue<string>()); 
new Thread(() => { 
    foreach (var item in queue.GetConsumingEnumerable()) { 
     // do heavy stuff with item 
    } 
}) { 
    IsBackground = true 
}.Start(); 
var w = new FileSystemWatcher(); 
// other stuff 
w.Changed += (sender, args) => 
{ 
    // takes no time, so overflow chance is drastically reduced 
    queue.Add(args.FullPath); 
}; 

あなたはまたに加入されていませんがErrorイベントがFileSystemWatcherであるため、何かが間違っているかどうかは分かりません。

+0

ありがとうございましたEvk少しピンキーした後、私はそれが仕事に持っている、ファイルがもう失われていないようです。 – user3260707

-1

信頼できるファイルモニターを使用する必要がある場合、USN Journalsを使用するという難しい方法を学びました。 https://stackoverflow.com/a/31931109/612717

ます。またflie長+も、LastModifiedDateは使用してタイマーポーリングでそれを手動自分で実装することができます:あなたが十分な権限を持っている場合、ここで

https://msdn.microsoft.com/en-us/library/windows/desktop/aa363798(v=vs.85).aspx

は、あなたがそれ.NETにアクセスできる方法です。

+1

変更日はそれ自身が信頼できないし、遅い*です。あなたが* ab *使用していない場合、FSWはうまく動作します。 AlphaFSのようなライブラリを使用していない限り、.NETはそのジャーナルにアクセスできません。*また、volumne全体でそれを有効にするために管理者権限を持ちます –

+0

また、ポーリングを使用するのが軽いということもわかりません。ほとんどの場合、ファイルが変更されたかどうかを知るために最終更新日+長さを使用するのに十分です。超高精度が要求される場合、ファイルストリームの最初の数ビットまたは最後の数ビットのmd5ハッシュを使用することができる。ジャーナルを読む方法を知る必要があります。いくつかの巨大なライブラリの必要はありません。 –

0

FSWのドキュメントでは、イベント処理に時間がかかりすぎると、イベントが失われる可能性があることが警告されています。そのため、常にキューやバックグラウンド処理で使用されます。

一つのオプションバックグラウンドで処理を実行するためにTask.Runを使用することです:私はない何でもdisplayTextログインの代わりに使用

public void OnChanged(object sender, FileSystemEventArgs e) 
{ 
    _logger.Info("File Added " + e.FullPath); 
    Task.Run(()=>xmlRead(e.FullPath)); 
} 

注意してください。別のスレッドからUIスレッドにアクセスすることはできません。進行状況をログに記録する場合は、ログライブラリを使用します。

IProgress< T>インターフェイスを使用して、長時間実行されているジョブの進行状況や、それを介して公開したいことを報告することもできます。 Progress< T>の実装では、プログレスオブジェクトを親スレッド(通常はUIスレッド)にマーシャリングするように注意します。

偶数さらに良い溶液はActionBlock< T>です。 ActionBlockには入力メッセージをキューに入れることができる入力バッファと、同時に実行できる操作の数を指定できるDOP設定があります。あなたは読書と電子メールで送信するためにdifferntブロックを作成し、パイプラインでそれらを接続することができ、

ActionBlock<string> _mailerBlock; 

public void Init() 
{ 
    var options=new ExecutionDataflowBlockOptions { 
     MaxDegreeOfParallelism = 5 
    }; 
    _mailerBlock = new ActionBlock<string>(path=>xlmRead(path),options); 
} 

public void OnChanged(object sender, FileSystemEventArgs e) 
{ 
    _logger.Info("File Added " + e.FullPath); 
    _mailerBlock.Post(e.FullPath); 
} 

いっそのこと:デフォルトは1です。この場合、ファイルリーダーはTransformManyBlockが必要とされることを意味し、電子メールの多くを生成します。

class EmailInfo 
{ 
    public string Data{get;set;} 
    public string Attachement{get;set;} 
} 


var readerBlock = new TransformManyBlock<string,EmailInfo>(path=>infosFromXml(path)); 

var mailBlock = new ActionBlock<EmailInfo>(info=>sendMailFromInfo(info)); 

readerBlock.LinkTo(mailBlock,new DataflowLinkOptions{PropagateCompletion=true}); 

xmlRead方法はにイテレータ

public IEnumerable<EmailInfo> infosFromXml(string path) 
{ 
    // Same as before ... 
    foreach (var r in photo_information) 
    { 
     if (r.user_data != "") 
     { 
      ... 
      yield return new EmailInfo{ 
         Data=r.user_data, 
         Attachment=attachmentFilename}; 
     } 
     ... 
    } 
} 

そしてsendmailに変更する必要があります。

public void sendMailFromInfo(EmailInfo info) 
{ 
    string email=info.Data; 
    string attachmentFilename=info.Attachment; 
} 

パイプラインを終了したいときは、ヘッドブロックでComplete()と呼び出し、テールの完了を待ちますに。これは、残りのすべてのファイルが処理されることを保証する:すべてそのコードを費やさ

readerBlock.Complete(); 
await mailerBlock.Completion; 
関連する問題