Как проверить, изменилось ли содержимое каталога с помощью PHP?
Я пишу фотогалерею script в PHP и имею единственный каталог, в котором пользователь будет хранить свои фотографии. Я пытаюсь настроить кеширование страниц и обновлять кеш, только если содержимое каталога изменилось. Я думал, что могу сделать это, кэшируя последнее измененное время в каталоге с помощью функции filemtime() и сравнивая его с текущим измененным временем каталога. Однако, как я понял, измененное время каталога не изменяется, поскольку файлы добавляются или удаляются из этого каталога (по крайней мере, в Windows, но не уверены в том, что Linux-машины еще не созданы).
Итак, мои вопросы: какой самый простой способ проверить, было ли изменено содержимое каталога?
Ответы
Ответ 1
Э. Я бы просто сохранил md5 в списке каталогов. Если содержимое изменится, изменится md5 (каталог-листинг). Вы можете получить очень случайное столкновение md5, но я думаю, что этот шанс достаточно крошечный.
Кроме того, вы можете сохранить небольшой файл в этом каталоге, который содержит дату последнего изменения. Но я бы пошел с md5.
PS. на второй взгляд, видя, как вы смотрите на выполнение (кэширование) запроса и хеширования, список каталогов может быть не совсем оптимальным.
Ответ 2
Как уже упоминалось другими, лучший способ решить эту задачу - вызвать функцию при возникновении определенных событий, которая изменит папку.
Однако, если ваш сервер является 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
Ответ 3
Как насчет касаясь каталога после того, как пользователь отправил его изображение?
Changelog говорит: Требуется php 5.3 для работы Windows, но я думаю, что он должен работать во всех других средах.
Ответ 4
с 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";
}
Ответ 5
Вот что вы можете попробовать. Храните все снимки в одном каталоге (или в подкаталогах /username
внутри него, чтобы ускорить работу и уменьшить нагрузку на FS), и настройте Apache (или whaterver, который вы используете), чтобы служить им как статический контент с "истекает" "в течение 100 лет в будущем". Имена файлов должны содержать уникальный префикс или суффикс (отметка времени, SHA1 хэш содержимого файла и т.д.), Поэтому всякий раз, когда использует изменения, имя файла изменяется, а Apache будет обслуживать новую версию, которая будет кэшироваться по пути.
Ответ 6
Ты думаешь не так.
Вы должны выполнить свой индексный указатель script, как только кто-то загрузил новый файл и переместился в целевое местоположение.
Ответ 7
IMO edubem answer - путь, но вы можете сделать что-то вроде этого:
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;
}
Ответ 8
Попробуйте удалить кешированную версию, когда пользователь загрузит файл в свой каталог.
Когда кто-то пытается просмотреть галерею, посмотрите, есть ли сначала кешированная версия. Если есть кешированная версия, загрузите ее, в противном случае создайте страницу, выполните кеширование, сделайте.
Ответ 9
Я искал что-то подобное, и я просто нашел это:
http://www.franzone.com/2008/06/05/php-script-to-monitor-ftp-directory-changes/
Для меня выглядит отличное решение, так как у меня будет большой контроль (я буду делать вызов AJAX, чтобы увидеть, что-нибудь изменилось).
Надеюсь, что это поможет.
Ответ 10
Вот пример кода, который будет возвращать 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');