PHPでバッチで処理する必要があるファイルを含むディレクトリがあります。ファイルはFTP経由でサーバーにコピーされます。一部のファイルは非常に大きく、コピーに時間がかかります。 PHP=ファイルがまだ転送されているかどうかを確認するにはどうすればよいですか(そのファイルの処理をスキップして、バッチプロセスの次の実行で処理できるように))。
可能性としては、ファイルサイズを取得し、しばらく待って、ファイルサイズが異なるかどうかを確認します。これは防水ではありません。転送が数分間停止した可能性があります。
サーバー管理者は、現在転送されているファイルを出力するftpwhoを提案しました。
http://www.castaglia.org/proftpd/doc/ftpwho.html
したがって、解決策は、ディレクトリ内のファイルが転送されているかどうかを確認するためにftpwhoの出力を解析することです。
これを行う最も安全な方法の1つは、一時的な名前でファイルをアップロードし、転送が完了したらファイル名を変更することです。プログラムは、一時的な名前のファイルをスキップする必要があります(単純な拡張子は問題なく機能します)。明らかに、これにはクライアント(アップローダー)の協力が必要なので、理想的ではありません。
[これにより、必要に応じて、一定期間後に失敗した(部分的な)転送を削除することもできます。]
ファイルサイズのポーリングに基づくものはすべて、際どいもので安全ではありません。
別のスキーム(アップローダーからの協力も必要です)では、最初にファイルのハッシュとサイズをアップロードしてから、実際のファイルをアップロードすることができます。これにより、転送がいつ行われたか、および一貫性があるかどうかの両方を知ることができます。 (このアイデアにはさまざまなバリエーションがあります。)
クライアントからの協力を必要としないものは、ファイルが別のプロセスによって開かれているかどうかをチェックしています。 (OSに依存する方法-これを行う組み込みのPHPビルトインは知りません。lsof
および/またはfuser
はさまざまなUnixタイプのプラットフォーム、WindowsにはこのためのAPIがあります。別のプロセスがファイルを開いている場合、まだ完了していない可能性があります。
アップロードの再開/再開を許可する場合、またはFTPサーバーソフトウェアが転送の全期間にわたってファイルを開いたままにしない場合、YMMVである場合、この最後のアプローチは確実ではない可能性があることに注意してください。
一部のFTPサーバーでは、特定のイベントが発生したときにコマンドを実行できます。したがって、FTPサーバーでこれが許可されている場合は、単純なシグナリングスキームを構築して、ファイルのアップロードが多かれ少なかれ成功したことをアプリケーションに通知できます(多かれ少なかれの理由は、ユーザーがファイルをアップロードしたかどうかわからないためです)完全にまたは部分的に)。信号方式は「uploaded_file_name.ext.complete」ファイルの作成と同じくらい簡単で、「。complete」拡張子が付いたファイルの存在を監視します。
これで、書き込み用にファイルを開くことができるかどうかを確認できます。ほとんどのFTPサーバーは、ファイルがアップロードされている場合、これを許可しません。
Matが言及したもう1つのアプローチは、システム固有の手法を使用して、ファイルが他のプロセスによって開かれているかどうかを確認することです。
チェックする最良の方法は、flockを使用してファイルを排他的にロックすることです。 sftp/ftpプロセスはfopenライブラリを使用します。
// try and get exclusive lock on file
$fp = fopen($pathname, "r+");
if (flock($fp, LOCK_EX)) { // acquire an exclusive lock
flock($fp, LOCK_UN); // release the lock
fclose($fp);
}
else {
error_log("Failed to get exclusive lock on $pathname. File may be still uploading.");
}
それは本当に素敵なトリックではありませんが、それは簡単です:-)、同じuはfilemtimeで行うことができます
$result = false;
$tryies = 5;
if (file_exists($filepath)) {
for ($i=0; $i < $tryies; $i++) {
sleep(1);
$filesize[] = filesize($filepath);
}
$filesize = array_unique($filesize);
if (count($filesize) == 1) {
$result = true;
} else {
$result = false;
}
}
return $result;