【问题标题】:Dynamically fetching css from CDN with fallback to local copy从 CDN 动态获取 css 并回退到本地副本
【发布时间】:2013-07-06 03:36:45
【问题描述】:

我正在尝试从外部 CDN 服务获取我的 CSS,比如说 http://cdn.example.com/

此代码用于检查外部 CDN 上是否存在文件,如果存在,则获取该文件,否则从本地文件夹获取。

目前这仅获取本地副本,即使该文件存在于外部 CDN 上。

请帮我更正这段代码。

function getCSSfile($filename)
{
    $externalcss = EXTERNAL_CDN.'css/'.$filename.'.css';
    $localcss = LOCAL_CDN.'css/'.$filename.'.css';

    $ctx = stream_context_create(array(
        'http' => array(
            'method' => 'HEAD'
        )
    ));

    if (file_get_contents($externalcss, false, $ctx) !== false) {
        echo '<link href="' . $externalcss . '" rel="stylesheet" type="text/css" media="all"/>';
    } else if (file_exists($localcss)) {
        echo '<link href="' . $localcss . '" rel="stylesheet" type="text/css" media="all"/>';
    } else {
        echo 'Error loading CSS File'; 
    }
}

更新:

根据@DaveRandom 的建议更新了代码。当前代码运行良好,它从外部 CDN 获取副本,但如果外部 CDN 不可用,它会获取本地副本也会引发以下错误:

Warning: file_get_contents(http://cdn.localtest.com/css/core.css): failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found in D:\xampp\htdocs\localtest2\core\core.php on line 165

在第 165 行,这是代码

if (file_get_contents($externalcss, false, $ctx) !== false) {

我在本地测试它并在我的 XAMPP 服务器上创建了一些域,因此无法共享实时链接。

【问题讨论】:

  • 你确定所有的 URL 都是正确的吗?您使用的实际 URL 是什么?
  • 旁注:最后一个 else 语句不应该存在,因为这会将错误回显到 HTML 中。或者,您可以回显 '&lt;!-- Error loading CSS File --&gt;' 以生成 HTML 注释。
  • @Alfred 感谢您的建议。我用它更正并更新了我的答案。但是问题仍然存在。

标签: php css function external


【解决方案1】:

我不相信您可以在 URL 上使用 file_existsthis answer 可能会阐明您可以用来正确检查返回文件的 URL 的代码。

但总而言之,您要检查返回给您的状态码,而不是使用file_exists

【讨论】:

  • 您可以使用一些网址 (ftp),但不能使用 httphttps 网址
  • 你会比我更了解,我实际上并不了解 PHP 我只是锻炼了我的谷歌肌肉,看看我是否能找到任何有用的东西。
【解决方案2】:

检查文件是否可以通过 PHP 访问的问题是检查是从您的服务器完成的,而不是客户端加载文件。仅仅因为您的服务器可以从 CDN 访问 CSS 文件并不意味着客户端可以。

如果您要使用回退进行检查,则使用 javascript 在客户端进行检查会更安全。

How to fallback to local stylesheet if CDN fails

【讨论】:

    【解决方案3】:

    另一个潜在的问题(除了@Johannes 的回答)是你的常量。在问题中,您将 CDN URL 构造为http://cdn.example.com。将此附加到 css/(如您的代码中)将生成 http://cdn.example.comcss/,这不是有效的 URL。

    确保常量(基本 URL)的结构为 http://cdn.example.com/


    编辑:要添加到@Johannes 的答案,我从 SO 上的另一个答案中找到了此链接:http://www.php.net/manual/en/function.file-exists.php#75064

    把代码放在这里:

    $file = 'http://www.domain.com/somefile.jpg';
    $file_headers = @get_headers($file);
    if($file_headers[0] == 'HTTP/1.1 404 Not Found') {
        $exists = false;
    }
    else {
        $exists = true;
    }
    

    【讨论】:

      【解决方案4】:

      在我看来,这个过程是错误的 - 如果本地副本存在(并且不是陈旧的,需要定义的机制),您应该提供本地副本并回退到 CDN。这意味着你的行为就像一个缓存。

      但是要直接回答这个问题,最好的办法是使用HEAD 请求 CDN 来查看它是否存在。对此使用 get_headers() 很有吸引力,但实际上这会导致 GET 方法请求,除非您更改默认流上下文 - 这会在您的应用程序中创建可能不需要的全局状态。

      所以我首选的方法是创建本地化流上下文并将其与将其作为参数接受的函数结合使用。为简单起见,在下面的示例中,我将使用file_get_contents()

      function getCSSfile($filename)
      {
          $externalcss = EXTERNAL_CDN.'css/'.$filename.'.css';
          $localcss = LOCAL_CDN.'css/'.$filename.'.css';
      
          $ctx = stream_context_create(array(
              'http' => array(
                  'method' => 'HEAD'
              )
          ));
      
          if (file_get_contents($externalcss, false, $ctx) !== false) {
              echo '<link href="' . $externalcss . '" rel="stylesheet" type="text/css" media="all"/>';
          } else if (file_exists($localcss)) {
              echo '<link href="' . $localcss . '" rel="stylesheet" type="text/css" media="all"/>';
          } else {
              echo 'Error loading CSS File'; 
          }
      }
      

      现在,这远非完美,但我从您的原始代码中回收了很多机制。仍然存在全局状态(在这样的函数中使用用户定义的常量对我来说是一个很大的禁忌)并且您仍然有一个直接产生输出而不是将控制权返回给调用者的函数。

      我更愿意将$externalcss$localcss 作为参数传递,并简单地返回所选的URL 字符串,而不是将其格式化为HTML 并回显它。这样,代码是隔离的,不依赖于任何外部,并且通过返回字符串,您允许调用者在必要时进一步操作数据。从本质上讲,它使代码的可重用性无限提高。

      旁注:虽然可能,但不建议在函数中定义函数。这是因为第二次调用会导致Cannot redeclare function 致命错误。

      【讨论】:

      • OK 此代码运行良好,但存在问题。 Warning: file_get_contents(http://cdn.localtest.com/css/core.css): failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found in D:\xampp\htdocs\localtest2\core\core.php on line 165 在第 165 行这是代码 if (file_get_contents($externalcss, false, $ctx) !== false) { 我在本地对其进行测试并在我的 XAMPP 服务器上创建了一些域,因此无法共享实时链接。
      • 我认为 OP 的意思是“本地副本”,就像服务器上的本地副本一样,而不是客户端计算机上的本地副本。由于服务器可能比 CDN 上的服务器更远和/或更差,因此他希望首先为 CDN 提供服务。
      【解决方案5】:

      好的,因为没有人解释错误解决方案......我决定回答我自己的问题。这是我找到的解决方案:

      function getCSSfile($filename)
      {
          $externalcss = EXTERNAL_CDN.'css/'.$filename.'.css';
          $localcss = LOCAL_CDN.'css/'.$filename.'.css';
      
          $ctx = stream_context_create(array(
              'http' => array(
                  'method' => 'HEAD'
              )
          ));
      
          if (@file_get_contents($externalcss, false, $ctx) !== false) {
              echo '<link href="' . $externalcss . '" rel="stylesheet" type="text/css" media="all"/>';
          } else if (file_exists($localcss)) {
              echo '<link href="' . $localcss . '" rel="stylesheet" type="text/css" media="all"/>';
          } else {
              echo 'Error loading CSS File'; 
          }
      }
      

      正如Mark B所建议的那样

      the docs 中所述,如果找不到指定的资源,file_get_contents() 将抛出警告。

      这是使用@ 错误抑制运算符可能是合理的少数情况之一,例如

      if (@file_get_contents($externalcss, false, $ctx) !== false)
      

      防止警告破坏输出。

      【讨论】:

        猜你喜欢
        • 2013-07-08
        • 2013-06-30
        • 2012-09-16
        • 2012-11-19
        • 1970-01-01
        • 1970-01-01
        • 2014-07-08
        • 2015-09-22
        • 1970-01-01
        相关资源
        最近更新 更多