4

我正在使用一个外部 Web 服务,该服务将返回一个图像 URL,我将在我的网站中显示该 URL,例如:

$url = get_from_web_service();
echo '<img url="'.$url.'" />';

一切正常,除非我有 100 张图像要显示,然后调用 Web 服务变得耗时和资源消耗。

//the problem
foreach($items as $item) {
   $url = get_from_web_service($item);
   echo '<img url="'.$url.'" />';
}

所以现在我正在考虑两种选择:

//Option1: Using php get_file_contents():
foreach($items as $item)
{
   echo '<img url="url_to_my_website/get_image.php?id='.$item->id.'" />'
}

get_image.php :

$url = get_from_web_service($id);
header("Content-Type: image/png");
echo file_get_contents($url);


//Option2: Using ajax:

echo '<img scr="dummy_image_or_website_logo" data-id="123" />';

//ajax call to the web service to get the id=123 and get the url then add the src attribute to that image.

想法

  1. 第一个选项似乎更直接,但我的服务器可能会超载并参与每个图像请求。
  2. 第二个选项都是由浏览器和网络服务完成的,所以我的服务器根本不参与。但是对于每张图片,我要进行 2 次调用 1 次 ajax 调用来获取图像 URL,再调用 1 次来获取图像。因此加载时间可能会有所不同,并且 ajax 调用可能会因大量调用而失败。

信息

  1. 该页面将显示大约 50 张图像。
  2. 在给定时间,大约有 100 个用户将使用此服务。
  3. 我无法控制 Web 服务,因此我无法更改其功能,并且每次调用不接受超过 1 个图像 ID。

我的问题

  1. 我应该考虑任何更好的选择吗?
  2. 如果没有,我应该遵循哪个选项?最重要的是为什么我应该遵循那个?

谢谢

4

9 回答 9

9

方法一:用 PHP 渲染

优点:

  • 允许独立于任何服务器软件的自定义标头。如果您正在使用通常不缓存的东西(例如带有查询字符串的 PHP 文件),或者将其添加到需要标头功能的包中,而不管服务器软件如何,这是一个非常好的主意。

  • 如果您知道如何使用GDImagick,您可以轻松地调整图像大小、裁剪、压缩、索引等,以减小图像文件大小(有时会大幅减少)并使页面加载速度显着加快。

  • 如果将宽度和高度作为变量传递给 PHP 文件,则可以动态设置尺寸:

    <div id="gallery-images">
        <noscript>
            <!-- So that the thumbnail is small for old mobile devices //-->
            <img src="get-image.php?id=123&h=200&w=200" />
        </noscript>
    </div>
    <script type="text/javascript">
        /* Something to create an image element inside of the div.
         * In theory, the browser height and width can be pulled dynamically
         * on page load, which is useful for ensuring that images are no larger
         * than they need to be. Having a function to load the full image
         * if the borwser becomes bigger isn't a bad idea though.
         */
    </script>
    

    对于具有图片库的页面上的移动用户来说,这将是非常体贴的。这对带宽有限的用户也很体贴(就像阿拉斯加的几乎每个人一样。我是根据个人经验说的)。

  • 允许您轻松清除网站上用户上传的图片的 EXIF 数据。这对于用户隐私以及确保 JPG 中没有任何恶意脚本非常重要。

  • 提供动态创建大型图像精灵的潜力,并在它们导致延迟时大大减少您的 HTTP 请求。这将是很多工作,所以这不是一个非常强大的专业人士,但使用这种方法仍然可以做到,而使用第二种方法则无法做到。

缺点

  • 根据图像的数量和大小,这可能会给您的服务器带来很大压力。当与浏览器缓存一起使用时,动态图像是从缓存中提取而不是重新生成的,但是对于机器人来说,多次提供动态图像仍然很容易。

  • 它需要 HTTP 标头知识、基本图像处理技能以及如何在 PHP 中使用图像处理库才能有效。

方法二:AJAX

优点:

  • 该页面将在任何图像之前完成加载。如果您的内容绝对需要尽可能快地加载,并且图像不是很重要,这很重要。

  • 比任何类型的动态 PHP 解决方案都更简单、容易且实施起来明显更快。

  • 它将 HTTP 请求隔开,因此初始内容加载速度更快(因为 HTTP 请求可以基于浏览器操作发送,而不仅仅是页面加载)。

缺点:

  • 它不会减少 HTTP 请求的数量,它只是将它们隔开。另请注意,除了所有这些图像之外,至少还有一个额外的外部 JS 文件。

  • 如果终端设备(例如较旧的移动设备)不支持 JavaScript,则不显示任何内容。解决此问题的唯一方法是让所有图像在某些<noscript>标签之间正常加载,这需要 PHP 生成两倍的 HTML。

  • 将要求您向页面添加loading.gif(和另一个 HTTP 请求)或Please wait while these images load文本。作为网站用户,我个人觉得这很烦人,因为我想在页面“加载完成”时查看所有内容。

结论:

如果您有背景知识或时间来学习如何有效地使用方法 1,它会提供更大的潜力,因为它允许在页面加载后对页面发送的图像和 HTTP 请求进行操作。

相反,如果您正在寻找一种简单的方法来分隔 HTTP 请求,或者希望通过稍后加载额外的图像来加快内容加载速度,那么方法 2 就是您的答案。

回顾方法 1 和 2,似乎同时使用这两种方法可能是最好的答案。让您的两个缓存和压缩图像随页面一起加载(一个是可见的,另一个是缓冲区,这样用户每次单击“下一步”时都不必等待),其余的则一个接一个地加载一个用户认为合适的。

在您的具体情况下,如果您的图像可以以“幻灯片”方式显示,我认为方法 2 将是最有效的。如果需要一次加载所有图像,请尝试压缩它们并使用方法 1 应用浏览器缓存。如果页面加载时过多的图像请求会破坏您的速度,请尝试图像分割。

于 2013-09-03T03:53:49.120 回答
3

截至目前,您正在联系网络服务 100 次。您应该更改它,使其仅与 Web 服务联系一次并检索所有 100 个图像的数组,而不是单独检索每个图像。

然后,您可以遍历这个数组,这将非常快,因为不需要进一步的 web 事务。

于 2013-09-05T09:02:37.307 回答
2

如果您从 web 服务获取的图像本质上不是动态的,即不经常更改/修改,我建议在您的服务器上设置一个计划的进程/cron 作业,该作业从 web 服务获取图像并在本地存储(在您的服务器本身),因此您可以仅在网页上显示来自服务器的图像,并避免每次将网页提供给最终用户时第三方服务器往返。

于 2013-09-09T16:30:35.760 回答
1

两个选项都无法解决您的问题,可能会使情况变得更糟。

对于选项 1:

花费时间最多的进程是“ get_from_web_service($item)”,并且代码仅由另一个脚本执行(如果文件“ get_image.php”在同一服务器上执行)。

对于选项 2:

它只会让浏览器触发“get-image-resource-request”,但您的服务器也需要处理“ get_from_web_service($item)”。

必须要明确一点,问题在于get_from_web_service的性能,最直接的建议是让它有更好的性能。另一方面,我们可以让它减少并发连接的数量。我还没想好,只有两个建议:

  1. 异步:用户没有浏览整个页面,他们只注意到顶部的页面。如果您提到的图像没有全部显示在顶部,您可以使用 jquery.lazyload 扩展名,它可以使不可见区域的图像资源在它们可见之前不请求服务器。

  2. CSS Sprites:图像精灵是一组图像放入单个图像中。如果您页面上的图像不改变频率,您可以编写一些代码来每天合并它们。

  3. 缓存图像:您可以将图像缓存在您的服务器或另一台服务器(更好)。并做一些 key->value 的工作:key 是关于 $item,value 是资源目录(url)。

我不是以英语为母语的人,希望我说得清楚并对您有所帮助。

于 2013-09-05T09:54:14.133 回答
0

As we see that above you're including an URL to the web service provided image right in the <img> tag src attribute, one can safely assume that these URLs are not secret or confidential.

Knowing that above, the following snippet from the get_image.php will work with the least overhead possible:

$url = get_from_web_service($id);
header("Location: $url");

If you're getting a lot of subsequent requests to the same id from a given client, you can somewhat lessen number of requests by exploiting browser's internal cache.

header("Cache-Control: private, max-age=$seconds");
header("Expires: ".gmdate('r', time()+$seconds));

Else resort to server-side caching by means of Memcached, database, or plain files like so:

is_dir('cache') or mkdir('cache');
$cachedDataFile = "cache/$id";
$cacheExpiryDelay = 3600; // an hour

if (is_file($cachedDataFile) && filesize($cachedDataFile) 
     && filemtime($cachedDataFile) + $cacheExpiryDelay > time()) {
    $url = file_get_contents($cachedDataFile);
} else {
    $url = get_from_web_service($id);
    file_put_contents($cachedDataFile, $url,  LOCK_EX);
}

header("Cache-Control: private, max-age=$cacheExpiryDelay");
header("Expires: ".gmdate('r', time() + $cacheExpiryDelay));
header("Location: $url");
于 2013-09-12T00:56:01.130 回答
0

选项一是最好的选择。我还想确保图像被缓存在服务器上,这样对于同一个图像,原始 Web 服务器不需要多次往返。

如果您感兴趣,这是我用于缓存图像等的代码的核心(请注意,缺少一些东西,例如将相同的内容保留回客户端等):

<?php
function error404() {
    header("HTTP/1.0 404 Not Found");
    echo "Page not found.";
    exit;
}

function hexString($md5, $hashLevels=3) {
    $hexString = substr($md5, 0, $hashLevels );
    $folder = "";

    while (strlen($hexString) > 0) {
        $folder =  "$hexString/$folder";
        $hexString = substr($hexString, 0, -1);
    }

    if (!file_exists('cache/' . $folder))
        mkdir('cache/' . $folder, 0777, true);

    return 'cache/' . $folder . $md5;
}

if (!isset($_GET['img']))
    error404();

getFile($_GET['img']);

function getFile($url) {
    // true to enable caching, false to delete cache if already cached
    $cache = true;

    $defaults = array(
        CURLOPT_HEADER => FALSE,
        CURLOPT_RETURNTRANSFER => 1,
        CURLOPT_FOLLOWLOCATION => 1,
        CURLOPT_MAXCONNECTS => 15,
        CURLOPT_CONNECTTIMEOUT => 30,
        CURLOPT_TIMEOUT => 360,
        CURLOPT_USERAGENT => 'Image Download'
    );

    $ch = curl_init();
    curl_setopt_array($ch, $defaults);
    curl_setopt($ch, CURLOPT_URL, $_GET['img']);

    $key  = hexString(sha1($url));
    if ($cache && file_exists($key)) {
        return file_get_contents($key);
    } elseif (!$cache && file_exists($key)) {
        unlink($key);
    }

    $data = curl_exec($this->_ch);
    $info = curl_getinfo($this->_ch);

    if ($cache === true && $info['http_code'] == 200 && strlen($data) > 20)
        file_put_contents($key, $data);
    elseif ($info['http_code'] != 200)
        error404();

    return $data;
}

$content = getURL($_GET['img']);
if ($content !== null or $content !== false) {
    // Success!
    header("Content-Type: image");
    echo $content;
}
于 2013-09-10T03:49:41.393 回答
0

我不是专家,但我认为每次你回声时,都需要时间。获得 100 张图像不应该是一个问题(唯一)

还。也许get_from_web_service($item);应该能够带一个数组?

$counter = 1;
$urls = array();
foreach($items as $item)
{
   $urls[$counter] = get_from_web_service($item);
   $counter++;
}
// and then you can echo the information?
foreach($urls as $url)
{
   //echo each or use a function to better do it
    //echo '<img url="url_to_my_website/get_image?id='.$url->id.'" />'

}

get_image.php :

$url = get_from_web_service($item);
header("Content-Type: image/png");
echo file_get_contents($url);

最后,如果你能打电话就太好了

get_from_web_service($itemArray); //intake the array and return images 
于 2013-09-03T04:07:18.087 回答
0

这两个选项都不能解决服务器资源使用问题。不过,在这两者中,我会推荐选项 1。第二个会延迟页面加载,导致网站速度变慢,并降低您的 SEO 评级。

对您来说最好的选择是:

foreach($items as $item) {
    echo '<img url="url_to_my_website/get_image.php?id='.$item->id.'" />'
}

那么魔法发生的地方就是get_image.php

if(file_exists('/path_to_local_storage/image_'.$id.'.png')) {
    $url = '/path_to_images_webfolder/image_'.$id.'.png';
    $img = file_get_contents($url);
} else {
    $url = get_from_web_service($id);
    $img = file_get_contents($url);
    $imgname = end(explode('/', $url));
    file_put_contents($imgname, $img);
}

header("Content-Type: image/png");
echo $img;

这是您将只对每个图像运行一次 Web 服务请求,然后将其存储在您的本地空间中。下次请求图像时 - 您将从本地空间提供图像,跳过对 Web 服务的请求。

当然,考虑到图像 ID 是唯一且持久的。

可能不是最好的解决方案,但对你来说应该很好。

于 2013-09-10T13:53:47.097 回答
0

选项 3:缓存对 Web 服务的请求

于 2013-09-06T21:13:17.980 回答