【问题标题】:5-minute file cache in PHPPHP 中的 5 分钟文件缓存
【发布时间】:2011-03-10 16:41:42
【问题描述】:

我有一个非常简单的问题:用 PHP 下载文件的最佳方式是什么,但前提是本地版本已在 5 分钟前下载完毕?

在我的实际情况中,我想从我目前使用的远程托管 csv 文件中获取数据

$file = file_get_contents($url);

没有任何本地副本或缓存。将其转换为缓存版本的最简单方法是什么,最终结果不会改变($file 保持不变),但如果它在不久前(比如 5 分钟)被获取,它会使用本地副本?

【问题讨论】:

    标签: php url curl download


    【解决方案1】:

    使用本地缓存文件,使用前只需检查文件的存在和修改时间。例如,如果$cache_file 是本地缓存文件名:

    if (file_exists($cache_file) && (filemtime($cache_file) > (time() - 60 * 5 ))) {
       // Cache file is less than five minutes old. 
       // Don't bother refreshing, just use the file as-is.
       $file = file_get_contents($cache_file);
    } else {
       // Our cache is out-of-date, so load the data from our remote server,
       // and also save it over our cache for next time.
       $file = file_get_contents($url);
       file_put_contents($cache_file, $file, LOCK_EX);
    }
    

    (未经测试,但基于我目前使用的代码。)

    无论通过此代码,$file 最终都是您需要的数据,如果它是新的,它将使用缓存,或者从远程服务器获取数据并刷新缓存。

    编辑:自从我写了上面的内容以来,我对文件锁定有了更多的了解。如果您担心此处的文件锁定,可能值得一读this answer

    如果您担心锁定和并发访问,我想说最干净的解决方案是将 file_put_contents 放到 临时 文件中,然后将 rename() 放在 $cache_file 上,这应该是原子操作,即$cache_file 将是旧内容或完整的新内容,永远不会写到一半。

    【讨论】:

    • 感谢代码马特!它超级干净,评论很好,无需任何修改即可工作!
    • @zsero 酷。但是一定要在那里进行一些错误检查:) 如果缓存目录不能被 Web 服务器用户写入,你可能会遇到问题,例如...
    • 是的,它可能需要一些错误检查,但它是一个如此小的项目,没有其他人使用或部署此代码。如果它坏了,else 部分实际上会进入无缓存模式,而不是减速。不错。
    • 确保在顶部定义您的 $cache_file(示例):$cache_file = $_SERVER['DOCUMENT_ROOT'] . '/my-cache.php';
    • @Volomike 据我了解,在每个脚本运行开始时都会清除统计缓存,所以只要您不在同一个脚本中多次调用此方法,应该没问题. (我刚刚检查过,在filestat.c 中,您会看到在PHP_RINIT_FUNCTION 回调中清除了统计缓存,因此它肯定会在每个请求开始时重置。)
    【解决方案2】:

    试试phpFastCache,它支持文件缓存,你不需要编写缓存类。易于在共享主机和 VPS 上使用

    示例如下:

    <?php
    
    // change files to memcached, wincache, xcache, apc, files, sqlite
    $cache = phpFastCache("files");
    
    $content = $cache->get($url);
    
    if($content == null) {
         $content = file_get_contents($url);
         // 300 = 5 minutes 
         $cache->set($url, $content, 300);
    }
    
    // use ur $content here
    echo $content;
    

    【讨论】:

    • 你能运行一个基准测试看看是否学说缓存更快或 phpFastCache 与文件缓存?
    【解决方案3】:

    这是一个简单的版本,它还将一个 windows User-Agent 字符串传递给远程主机,这样你就不会像没有正确标题的麻烦制造者。

    <?php
    
    function getCacheContent($cachefile, $remotepath, $cachetime = 120){
    
        // Generate the cache version if it doesn't exist or it's too old!
        if( ! file_exists($cachefile) OR (filemtime($cachefile) < (time() - $cachetime))) {
    
            $options = array(
                'method' => "GET",
                'header' => "Accept-language: en\r\n" .
                "User-Agent: Mozilla/5.0 (Windows; U; MSIE 7.0; Windows NT 6.0; en-US)\r\n"
            );
    
            $context = stream_context_create(array('http' => $options));
            $contents = file_get_contents($remotepath, false, $context);
    
            file_put_contents($cachefile, $contents, LOCK_EX);
            return $contents;
    
        }
    
        return file_get_contents($cachefile);
    }
    

    【讨论】:

      【解决方案4】:

      如果您使用任何类型的数据库系统,您可以将这个文件缓存在那里。为缓存信息创建一个表,并至少提供以下字段:

      • 标识符;下次需要时可以用来检索文件的东西。可能类似于文件名。
      • 上次从 URL 下载文件的时间戳。
      • 要么是文件的路径,它存储在本地文件系统中,要么使用 BLOB 类型字段将文件本身的内容存储在数据库中。我个人建议只存储路径。如果文件非常大,您肯定不想将其放入数据库中。

      现在,当您下次运行上面的脚本时,首先在数据库中检查标识符,然后提取时间戳。如果当前时间与存储的时间戳之间的差异大于 5 分钟,则从 URL 中提取并更新数据库。否则,从数据库中加载文件。

      如果您没有数据库设置,您可以只使用文件来做同样的事情,其中​​一个文件或文件中的字段将包含您上次下载文件时的时间戳。

      【讨论】:

        【解决方案5】:

        首先,您可能需要检查设计模式:Lazy loading

        实现应更改为始终从本地缓存加载文件。 如果本地缓存不存在或文件时间抖动超过5分钟,则从服务器获取文件。

        伪代码如下:

        $time = filetime($local_cache)
        if ($time == false || (now() - $time) > 300000)
             fetch_localcache($url)  #You have to do it yourself
        $file = fopen($local_cache)
        

        【讨论】:

          【解决方案6】:

          您可以在第一次点击时保存文件副本,然后使用 filemtime 检查后续点击时本地文件的最后修改的时间戳。

          【讨论】:

            【解决方案7】:

            你可以把它变成一个类似缓存的方法:

            function getFile($name) {
                // code stolen from @Peter M
                if ($file exists) {
                  if ($file time stamp older than 5 minutes) {
                     $file = file_get_contents($url)
                  }
                } else {
                     $file = file_get_contents($url)
                }
                return $file;
            }
            

            【讨论】:

            • 和 Peter M 的回答一样,我不知道为什么 if 和 else 是一样的?
            【解决方案8】:

            我认为您需要一些(伪代码)逻辑,例如:

            if ($file exists) {
              if ($file time stamp older than 5 minutes) {
                 $file = file_get_contents($url)
              }
            } else {
                 $file = file_get_contents($url)
            }
            
            use $file
            

            【讨论】:

            • @zsero .. 额外的层在那里,因为您无法测试不存在的文件的时间戳。
            猜你喜欢
            • 2020-03-23
            • 2019-10-26
            • 2011-10-13
            • 2011-08-12
            • 1970-01-01
            • 2014-01-20
            • 1970-01-01
            • 2011-09-28
            • 1970-01-01
            相关资源
            最近更新 更多