質問
FileSystemWatcher を使用して、ディレクトリ内の新しいファイルを監視する .NET アプリケーションがいくつかあります。ファイルは別の場所からコピーされたり、FTP 経由でアップロードされたりします。ファイルが到着すると、何らかの方法で処理されます。ただし、満足のいく答えが見つかったことがない問題が 1 つあります。大きなファイルの場合、監視対象のファイルがまだ書き込み中であることをどうやって知ることができるでしょうか?明らかに、ファイルの処理を開始する前に、ファイルが完了して閉じるまで待つ必要があります。FileSystemWatcher イベントのイベント引数はこれに対処していないようです。
解決
ファイルに書き込みロックをかけてみましたか?書き込み中の場合は失敗するはずなので、しばらく放置しておくとよいでしょう...
他のヒント
ディレクトリにファイルを書き込むプログラムを制御している場合は、プログラムにファイルを一時ディレクトリに書き込んでから、監視対象のディレクトリに移動させることができます。移動はアトミックな操作である必要があるため、ファイルがディレクトリに完全に配置されるまでウォッチャーはファイルを確認できません。
監視対象のディレクトリに何を書き込むかを制御できない場合は、ファイルが一定時間同じサイズを維持したときにファイルが完了したとみなされる時間をウォッチャーで設定できます。即時処理が問題でない場合は、このタイマーを比較的大きな値に設定すると、ファイルが完了したか、完了しないかを知るためのかなり安全な方法になります。
FileSystemWatcher の「Changed」イベントは、ファイルが閉じられるまで発生しないようにする必要があります。私のことを見てください 同様の質問に対する答え. 。新しいデータが入ってくると、FTP ダウンロード メカニズムがダウンロード中にファイルを複数回閉じる可能性がありますが、その可能性は少し低いと思います。
ファイルの内容が完了しているかどうかを検証できない限り (検証可能な形式であるか、内容のチェックサムが含まれている場合)、ファイル全体が到着したことを確認できるのは送信者だけです。
私は以前、FTP 経由で大きなファイルを送信するためにロック方式を使用しました。
ファイルは別の拡張子で送信され、送信者がすべての内容を確認すると名前が変更されます。
上記は明らかに、一時的な拡張子を持つ古いファイルを定期的に整理するプロセスと組み合わされています。
別の方法としては、同じ名前で拡張子 .lck を追加した長さ 0 のファイルを作成することもできます。実際のファイルが完全にアップロードされると、lck ファイルは削除されます。受信プロセスは、ロック ファイルの名前を持つファイルを明らかに無視します。
このようなシステムがなければ、受信者はファイル全体が到着したことを確信できません。
x 分間変更されていないファイルをチェックすると、さまざまな問題が発生する可能性があります。
次のメソッドは、書き込み権限でファイルを開こうとします。ファイルがディスクに完全に書き込まれるまで実行がブロックされます。
/// <summary>
/// Waits until a file can be opened with write permission
/// </summary>
public static void WaitReady(string fileName)
{
while (true)
{
try
{
using (System.IO.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;
}
}
}
catch (FileNotFoundException ex)
{
System.Diagnostics.Trace.WriteLine(string.Format("Output file {0} not yet ready ({1})", fileName, ex.Message));
}
catch (IOException ex)
{
System.Diagnostics.Trace.WriteLine(string.Format("Output file {0} not yet ready ({1})", fileName, ex.Message));
}
catch (UnauthorizedAccessException ex)
{
System.Diagnostics.Trace.WriteLine(string.Format("Output file {0} not yet ready ({1})", fileName, ex.Message));
}
Thread.Sleep(500);
}
}
(Aに対する私の答えから) 関連する質問)
おそらく帯域外シグナリングを使用する必要があるでしょう。「file.ext」の作成者にダミーの「file.ext.end」を書き込ませます。
可能であれば、file.ext.end シグナラーを使用する場合は +1。file.ext.end の内容は、より大きなファイルのチェックサムです。これはセキュリティのためというよりは、途中で何かが文字化けしないようにするためです。誰かが自分のファイルを大きなストリームに挿入できる場合、チェックサムも置き換えることができます。
ファイルのアップロードが途中で失敗し、送信者がまだファイルの再送信 (および再ロック) を試みていない場合、書き込みロックは役に立ちません。
Windows でファイルが FTP によって完全にアップロードされたかどうかを確認する方法は、ファイルの名前を変更してみることです。名前の変更が失敗した場合、ファイルは不完全です。あまりエレガントではないことは認めますが、機能します。