如何使用 PHP 检查目录内容是否已更改?

发布于 2024-07-14 01:44:30 字数 260 浏览 4 评论 0 原文

我正在用 PHP 编写一个照片库脚本,并且有一个目录,用户将在其中存储他们的图片。 我正在尝试设置页面缓存,并仅在目录内容发生更改时才刷新缓存。 我想我可以通过使用 filemtime() 函数缓存目录的上次修改时间并将其与目录的当前修改时间进行比较来做到这一点。 然而,正如我逐渐意识到的,当文件被添加到该目录或从该目录中删除时,目录修改时间不会改变(至少在 Windows 上,还不确定 Linux 机器上)。

所以我的问题是,检查目录内容是否已修改的最简单方法是什么?

I'm writing a photo gallery script in PHP and have a single directory where the user will store their pictures. I'm attempting to set up page caching and have the cache refresh only if the contents of the directory has changed. I thought I could do this by caching the last modified time of the directory using the filemtime() function and compare it to the current modified time of the directory. However, as I've come to realize, the directory modified time does not change as files are added or removed from that directory (at least on Windows, not sure about Linux machines yet).

So my questions is, what is the simplest way to check if the contents of a directory have been modified?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

贪了杯 2024-07-21 01:44:30

正如其他人已经提到的,解决此问题的更好方法是在发生特定事件时触发一个函数,从而更改文件夹。
但是,如果您的服务器是 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

As already mentioned by others, a better way to solve this would be to trigger a function when particular events happen, that changes the folder.
However, if your server is a unix, you can use inotifywait to watch the directory, and then invoke a PHP script.

Here's a simple example:

#!/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

See also: http://linux.die.net/man/1/inotifywait

我的痛♀有谁懂 2024-07-21 01:44:30

在用户提交图像后触摸目录怎么样?
更改日志说:Windows 需要 php 5.3 才能工作,但我认为它应该适用于所有其他环境

What about touching the directory after a user has submitted his image?
Changelog says: Requires php 5.3 for windows to work, but I think it should work on all other environments

梦断已成空 2024-07-21 01:44:30

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

with inotifywait inside 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";
}
ゃ懵逼小萝莉 2024-07-21 01:44:30

呃。 我只是存储目录列表的 md5。 如果内容改变,md5(目录列表)也会改变。 您可能偶尔会遇到 md5 冲突,但我认为这种机会很小..
或者,您可以在该目录中存储一个包含“上次修改”日期的小文件。 但我会选择 md5。


附言。 再想一想,看看您如何看待请求和散列目录列表的性能(缓存)可能并不完全是最佳的。

Uh. I'd simply store the md5 of a directory listing. If the contents change, the md5(directory-listing) will change. You might get the very occasional md5 clash, but I think that chance is tiny enough..
Alternatively, you could store a little file in that directory that contains the "last modified" date. But I'd go with md5.


PS. on second thought, seeing as how you're looking at performance (caching) requesting and hashing the directory listing might not be entirely optimal..

神妖 2024-07-21 01:44:30

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

IMO edubem's answer is the way to go, however you can do something like this:

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

Or a more weak / faster version:

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

Here are the functions used:

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;
}
蘑菇王子 2024-07-21 01:44:30

您可以尝试以下方法。 将所有图片存储在一个目录中(或在其中的 /username 子目录中,以加快速度并减轻 FS 的压力)并设置 Apache(或您正在使用的任何内容)来为它们提供服务作为静态内容,“过期”设置为未来 100 年。 文件名应该包含一些唯一的前缀或后缀(时间戳、文件内容的 SHA1 哈希值等),因此每当使用更改时,文件的名称都会更改,Apache 将提供新版本,该版本将一路缓存。

Here's what you may try. Store all pictures in a single directory (or in /username subdirectories inside it to speed things up and to lessen the stress on the FS) and set up Apache (or whaterver you're using) to serve them as static content with "expires-on" set to 100 years in the future. File names should contain some unique prefix or suffix (timestamp, SHA1 hash of file content, etc), so whenever uses changes the file its name gets changed and Apache will serve a new version, which will get cached along the way.

谜泪 2024-07-21 01:44:30

你的想法是错误的。

当有人上传新文件并将其移动到目标位置时,您应该立即执行目录索引器脚本。

You're thinking the wrong way.

You should execute your directory indexer script as soon as someone's uploaded a new file and it's moved to the target location.

寻梦旅人 2024-07-21 01:44:30

当用户将文件上传到其目录时,尝试删除缓存版本。

当有人尝试查看图库时,请先查看是否有缓存版本。 如果有缓存版本,则加载它,否则,生成页面,缓存它,完成。

Try deleting the cached version when a user uploads a file to his directory.

When someone tries to view the gallery, look if there's a cached version first. If there's a cached version, load it, otherwise, generate the page, cache it, done.

风情万种。 2024-07-21 01:44:30

我正在寻找类似的东西,我刚刚发现了这个:

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

对我来说看起来是一个很好的解决方案,因为我将有很多控制权(我将进行 AJAX 调用以查看是否有任何变化)。

希望这有帮助。

I was looking for something similar and I just found this:

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

For me looks like a great solution since I'll have a lot of control (I'll be doing an AJAX call to see if anything changed).

Hope that this helps.

三人与歌 2024-07-21 01:44:30

这是一个代码示例,如果目录更改,它将返回 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');

Here is a code sample, that would return 0 if the directory was changed.
I use it in backups.

The changed status is determined by presence of files and their filesizes.
You could easily change this, to compare file contents by replacing

$longString .= filesize($file);

with

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

but it will affect execution speed.

#!/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');
和我恋爱吧 2024-07-21 01:44:30

很简单。 当用户通过表单提交上传或更改某些内容时,然后在某处创建一个小文件(即 newDetected.txt)。 然后在页面加载时检查 file_exists(newDetected.txt)
取消链接(newDetected.txt)和
运行 recreate 函数来重建目录树。

very simple. when user uploads or alters something via form submissions then create a small file (i.e. newDetected.txt) in somewhere. then on page load check if file_exists(newDetected.txt)
unlink(newDetected.txt) and
run recreate function to rebuild dir tree.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文