Как проверить, изменилось ли содержимое каталога с помощью PHP?

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

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

10 ответов


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


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


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


насчет трогательно каталог после того, как пользователь отправил свое изображение? Changelog говорит: требуется php 5.3 для работы windows, но я думаю, что он должен работать во всех других средах


С 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";
}

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


ты думаешь неправильно.

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


ИМО edubem это!--5--> - это путь, но вы можете сделать что-то вроде этого:

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;
}

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

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


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

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

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

надеюсь, что это помогает.


вот пример кода, который возвращает 0, если каталог был изменен. Я использую его в резервных копиях.

измененный статус определяется наличием файлов и их filesizes. Вы можете легко изменить это, чтобы сравнить содержимое файла, заменив

$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');