ファイルの更新完了を検知する


ここ2ヶ月perlのお仕事をしています。こんなにPerl書いた事はなかったのですがそれなりに使えますね。ライブラリが充実してるのでなるべくライブラリに任すと簡単に処理が書けます。今回はファイル更新の検知について割とうまく書けたのでメモしておきます。
まずファイル更新検知ですが、Win32::ChangeNotifyを使う事でファイル検知ができます。$filterは検知する対象です。今回はファイルとフォルダの生成、削除のみ検知することにしました。

my $filter = "FILE_NAME DIR_NAME";
my $notify = Win32::ChangeNotify->new($targetPath, $flgSubTree, $filter);
if( $notify->wait( $timeout_Notify ) ){
#ファイル一覧取得
#更新処理
}

ここで検知してるのはファイル作成等の場合なので当然ファイル更新中もありえます。通常は他フォルダにファイルを更新して更新が終わったらフォルダ毎moveしたりします。しかし今回は仕様上作成中のファイルも扱う事になってしまいました。よってファイルが書き込み中でも検知してしまいます。これを解決するためにファイルが更新中なのかどうかを検知します。
File-Download-and-Password-Generator-Class-Update

ファイルが更新中かどうか色々考えた挙句同じ名前にリネームすることにしました。今回のターゲットはWindowsですが他のOSでもオープン中のファイルはリネームに失敗すると思います。なおかつリネームはシステムコールなので実施するコストが極めて低いと考えられます。
sub wait_until_close
{
$file = shift;
$wait = 0.2;
$time_out = 3;
until(not -e $file or my $stat = rename($file,$file) or $spend_time > $time_out ){
select(undef,undef,undef,$wait);
$spend_time += $wait;
}
return ($spend_time < $time_out ); }

ミソは同じファイル名にrenameするところ。ファイルのオープン中ではrenameは許可されません。なおかつファイル更新日時等は変更されません。また成功しても副作用がありません。更新日などはそのまま保存されます。
selectはperl版の1秒以下sleepです。よってタイムアウト、ファイル消去、ファイルがクローズされるまではループを抜けません。
この方法は他の言語でもOSでも利用できるでしょう。非同期更新に悩んでいる場合には検討されてはいかがでしょうか。


ファイルの更新完了を検知する」への1件のフィードバック

  1. > 他のOSでもオープン中のファイルはリネームに失敗すると思います。

    Linux を含む Unix 系のOSでは成功します。
    ちなみに削除も可。

    > システムコールなので実施するコストが極めて低いと考えられます。

    何に比べてがよくわかりませんが、システムコールは一般に高コストです。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください