Как проверить, изменилось ли содержимое каталога с помощью PHP? - PullRequest
23 голосов
/ 12 февраля 2009

Я пишу скрипт фотогалереи на PHP и у меня есть один каталог, где пользователь будет хранить свои картинки. Я пытаюсь настроить кеширование страниц и обновлять кеш, только если содержимое каталога изменилось. Я думал, что смогу сделать это, кэшируя время последнего изменения каталога с помощью функции filemtime () и сравнивая его с текущим временем изменения каталога. Однако, как я понял, время изменения каталога не меняется, когда файлы добавляются или удаляются из этого каталога (по крайней мере, в Windows, пока не уверен насчет машин Linux).

Итак, мои вопросы: как проще всего проверить, было ли изменено содержимое каталога?

Ответы [ 10 ]

22 голосов
/ 12 февраля 2009

Как уже упоминалось другими, лучший способ решить эту проблему - вызвать функцию, когда происходят определенные события, которая меняет папку. Однако, если ваш сервер является 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

8 голосов
/ 12 февраля 2009

Как насчет касания каталога после того, как пользователь отправил свое изображение? Список изменений говорит: Требуется PHP 5.3 для работы Windows, но я думаю, что он должен работать во всех других средах

6 голосов
/ 21 июля 2011

с inotifywait внутри php

$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 голосов
/ 26 июня 2009

Э. Я бы просто сохранил md5 списка каталогов. Если содержимое меняется, md5 (каталог-листинг) изменится. Вы могли бы получить очень случайное столкновение md5, но я думаю, что этот шанс достаточно мал ..
Кроме того, вы можете сохранить небольшой файл в этом каталоге, который содержит дату «последнего изменения». Но я бы пошел с MD5.


PS. если подумать, то, как вы смотрите на производительность (кеширование) запросов и хэширования списка каталогов, может быть не совсем оптимальным.

3 голосов
/ 12 февраля 2009

Ты ошибаешься.

Вам следует выполнить скрипт индексатора каталога, как только кто-то загрузит новый файл и он переместится в целевое местоположение.

3 голосов
/ 12 февраля 2009

Вот что вы можете попробовать. Храните все изображения в одном каталоге (или в подкаталогах /username внутри него, чтобы ускорить процесс и уменьшить нагрузку на FS) и настроить Apache (или другой используемый вами), чтобы они служили статическим контентом с «expires» -он "установлен на 100 лет в будущем. Имена файлов должны содержать некоторый уникальный префикс или суффикс (временная метка, SHA1-хэш содержимого файла и т. Д.), Поэтому при каждом изменении файла его имя изменяется, и Apache будет обслуживать новую версию, которая будет кешироваться по пути.

2 голосов
/ 13 декабря 2009

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;
}
1 голос
/ 09 ноября 2013

Вот пример кода, который вернул бы 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 голос
/ 13 декабря 2009

Я искал что-то подобное, и я только что нашел это:

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

Для меня это отличное решение, так как у меня будет много контроля (я буду делать вызов AJAX, чтобы увидеть, изменилось ли что-нибудь).

Надеюсь, что это поможет.

1 голос
/ 12 февраля 2009

Попробуйте удалить кэшированную версию, когда пользователь загружает файл в свой каталог.

Когда кто-то пытается просмотреть галерею, сначала посмотрите, есть ли кэшированная версия. Если есть кэшированная версия, загрузите ее, в противном случае создайте страницу, кэшируйте ее, готово.

...