web-dev-qa-db-ja.com

PHPでディレクトリの内容が変更されたかどうかを確認するにはどうすればよいですか?

PHPでフォトギャラリースクリプトを作成していて、ユーザーが写真を保存するディレクトリが1つあります。ページキャッシュを設定し、コンテンツが含まれている場合にのみキャッシュを更新しようとしています。ディレクトリのが変更されました。filemtime()関数を使用してディレクトリの最終変更時刻をキャッシュし、ディレクトリの現在の変更時刻と比較することでこれを実行できると思いました。しかし、私が気付いたように、ディレクトリの変更時間は、ファイルがそのディレクトリに追加または削除されても変更されません(少なくとも、Windowsでは、Linuxマシンについてはまだわかりません)。

だから私の質問は、ディレクトリの内容が変更されているかどうかを確認する最も簡単な方法は何ですか?

23
PHLAK

ええと。ディレクトリリストのmd5を保存するだけです。内容が変わると、md5(directory-listing)も変わります。あなたかもしれない非常に時折md5の衝突が発生しますが、チャンスは十分に小さいと思います。
または、「最終変更日」の日付を含む小さなファイルをそのディレクトリに保存することもできます。しかし、私はmd5を使います。


PS。考え直してみると、ディレクトリリストの要求とハッシュのパフォーマンス(キャッシュ)をどのように見ているかを見ると、完全に最適ではない可能性があります。

6
MSpreij

他の人がすでに述べたように、これを解決するためのより良い方法は、特定のイベントが発生したときにフォルダーを変更する関数をトリガーすることです。ただし、サーバーがUNIXの場合は、inotifywaitを使用してディレクトリを監視してから、PHPスクリプトを呼び出すことができます。

簡単な例を次に示します。

#!/bin/sh
inotifywait --recursive --monitor --quiet --event modify,create,delete,move --format '%f' /path/to/directory/to/watch |
  while read FILE ; do
    php /path/to/trigger.php $FILE
  done

参照: http://linux.die.net/man/1/inotifywait

22
troelskn

touching ユーザーが画像を送信した後のディレクトリはどうですか?変更ログによると:Windowsが機能するにはphp 5.3が必要ですが、他のすべての環境でも機能するはずです。

8
edubem

pHP内のinotifywaitを使用

$watchedDir = 'watch';

$in = popen("inotifywait --monitor --quiet --format '%e %f' --event create,moved_to '$watchedDir'", 'r');
if ($in === false)
    throw new Exception ('fail start notify');

while (($line = fgets($in)) !== false) 
{
    list($event, $file) = explode(' ', rtrim($line, PHP_EOL), 2);
    echo "$event $file\n";
}
6
chic

あなたは間違った方法を考えています。

誰かが新しいファイルをアップロードし、それがターゲットの場所に移動したらすぐに、ディレクトリインデクサースクリプトを実行する必要があります。

3
SchizoDuckie

これがあなたが試すかもしれないことです。すべての画像を1つのディレクトリ(または/usernameその中のサブディレクトリは、物事をスピードアップし、FSへのストレスを軽減します)、Apache(または使用しているもの)をセットアップして、静的コンテンツとして提供し、「有効期限」を100年後に設定します。ファイル名には一意のプレフィックスまたはサフィックス(タイムスタンプ、ファイルコンテンツのSHA1ハッシュなど)を含める必要があるため、ファイルを使用するたびにファイル名が変更され、Apacheは新しいバージョンを提供します。これは途中でキャッシュされます。

3
Anton Gogolev

IMO edubemの答え が進むべき道ですが、次のようなことができます。

if (sha1(serialize(Map('/path/to/directory/', true))) != /* previous stored hash */)
{
    // directory contents has changed
}

または、より弱い/より速いバージョン:

if (Size('/path/to/directory/', true) != /* previous stored size */)
{
    // directory contents has changed
}

使用される関数は次のとおりです。

function Map($path, $recursive = false)
{
    $result = array();

    if (is_dir($path) === true)
    {
        $path = Path($path);
        $files = array_diff(scandir($path), array('.', '..'));

        foreach ($files as $file)
        {
            if (is_dir($path . $file) === true)
            {
                $result[$file] = ($recursive === true) ? Map($path . $file, $recursive) : $this->Size($path . $file, true);
            }

            else if (is_file($path . $file) === true)
            {
                $result[$file] = Size($path . $file);
            }
        }
    }

    else if (is_file($path) === true)
    {
        $result[basename($path)] = Size($path);
    }

    return $result;
}

function Size($path, $recursive = true)
{
    $result = 0;

    if (is_dir($path) === true)
    {
        $path = Path($path);
        $files = array_diff(scandir($path), array('.', '..'));

        foreach ($files as $file)
        {
            if (is_dir($path . $file) === true)
            {
                $result += ($recursive === true) ? Size($path . $file, $recursive) : 0;
            }

            else if (is_file() === true)
            {
                $result += sprintf('%u', filesize($path . $file));
            }
        }
    }

    else if (is_file($path) === true)
    {
        $result += sprintf('%u', filesize($path));
    }

    return $result;
}

function Path($path)
{
    if (file_exists($path) === true)
    {
        $path = rtrim(str_replace('\\', '/', realpath($path)), '/');

        if (is_dir($path) === true)
        {
            $path .= '/';
        }

        return $path;
    }

    return false;
}
2
Alix Axel

私は似たようなものを探していました、そして私はちょうどこれを見つけました:

http://www.franzone.com/2008/06/05/php-script-to-monitor-ftp-directory-changes/

私にとっては、多くの制御ができるので、優れたソリューションのように見えます(AJAX呼び出しを行って何かが変更されたかどうかを確認します)。

これがお役に立てば幸いです。

1
Moises Kirsch

ユーザーが自分のディレクトリにファイルをアップロードするときに、キャッシュされたバージョンを削除してみてください。

誰かがギャラリーを表示しようとするときは、最初にキャッシュされたバージョンがあるかどうかを確認してください。キャッシュされたバージョンがある場合はそれをロードし、そうでない場合はページを生成してキャッシュします。

1
Mario

これはコードサンプルで、ディレクトリが変更された場合は0を返します。バックアップに使用しています。

変更されたステータスは、ファイルの存在とそのファイルサイズによって決まります。これを簡単に変更して、置き換えることでファイルの内容を比較できます

$longString .= filesize($file);

$longString .= crc32(file_get_contents($file));

ただし、実行速度に影響します。

#!/usr/bin/php
<?php

$dirName = $argv[1];
$basePath = '/var/www/vhosts/majestichorseporn.com/web/';
$dataFile = './backup_dir_if_changed.dat';

# startup checks
if (!is_writable($dataFile))
    die($dataFile . ' is not writable!');

if (!is_dir($basePath . $dirName))
    die($basePath . $dirName . ' is not a directory');

$dataFileContent = file_get_contents($dataFile);
$data = @unserialize($dataFileContent);
if ($data === false)
    $data = array();

# find all files ang concatenate their sizes to calculate crc32
$files = glob($basePath . $dirName . '/*', GLOB_BRACE);

$longString = '';
foreach ($files as $file) {
    $longString .= filesize($file);
}
$longStringHash = crc32($longString);

# do changed check
if (isset ($data[$dirName]) && $data[$dirName] == $longStringHash)
    die('Directory did not change.');

# save hash do DB
$data[$dirName] = $longStringHash;

file_put_contents($dataFile, serialize($data));
die('0');
1
Aldekein