PHP脚本显示图像BLOB执行两次?

4

我无法简明地总结标题中的问题,让我详细说明一下:

我有一个网站,使用PHP脚本“getImg.php”从我的数据库中获取图像二进制对象,并在调用HTML img标签时在单独的页面上显示它。

我想记录人们查看图像的次数,因此我添加了一条简单的代码来增加相应图像的“views”属性。

我认为这很简单,但事实证明它会增加两次。我的解决办法是将“views”列设置为浮点数,并通过增加0.5来得到1个增量。然而,我今天查看我的数据库发现有些图像中出现了0.5!

当我注释掉最后的打印语句时,它就可以正常工作。我以为来自HTML标记的调用加上脚本本身计算为两个调用?但其他人似乎并非如此。

这只是我的设置问题吗?

<?php

# Connect to db
include('db.php');

# Get ID
$id = $_GET['id'];
if(!is_numeric($id)) exit;

$q = $db->prepare("SELECT tNail,image,format FROM gallery WHERE id = '$id'");
$q->execute();
$row = $q->fetch(PDO::FETCH_ASSOC);

if(array_key_exists("thumb", $_GET))
    $img = base64_decode($row["tNail"]);
else
{
    $img = base64_decode($row["image"]);

    # Add to views if not thumb
    // 0.5 because script called twice due to print?
    $db->query("UPDATE gallery SET views = (views + 0.5) WHERE id = '$id'");
}

switch($row["format"])
{
    case ".jpg":
        header("Content-type: image/jpeg");
        break;
    case ".png":
        header("Content-type: image/png");
        break;
}

print $img;

?>

感谢您的任何帮助。

为什么要专门列出文件扩展名呢?读取文件加载时的 mime 类型不就可以了吗?此外,缩略图无需作为单独的列存储,你应该让 PHP 生成一个调整大小的图像。以上所有方法都可以节省很多数据库空间,并且更加高效。 - Austin
个人偏好。我喜欢使用print而不是echo哈哈 - Lee
但是使用echo会出现同样的问题吗? - Austin
那是个好问题,我会试一下。我不会期望它成功,因为它们之间的差别非常微小。 - Lee
也许这是浏览器问题?你测试过哪些浏览器?像 Chrome 这样的浏览器可能会预取图像,因此它只加载一次,如果用户点击它,则会加载两次。 - Besnik
显示剩余7条评论
2个回答

3

这似乎是一个浏览器问题。像 Chrome 这样的浏览器可以预取图像,因此只加载一次,如果用户单击链接到图像,则会加载两次。

检查

使用浏览器加载图像,然后检查 Apache 的 access.log,如果同时看到 2 个请求,则是浏览器问题。


你说得对。这在FF 14.0.1上表现不正确,但在K-meleon上很好。这是我无法避免的吗? - Lee
谢谢,是的它正在请求两个请求。 - Lee

2
这是您的问题:

以下是您的问题:

if(array_key_exists("thumb", $_GET))
    $img = base64_decode($row["tNail"]);
else
{$img = base64_decode($row["image"]);//<==

您正在以一行代码的方式启动此if...else分支,但在else情况下,您正在执行一个代码块。php从if下面执行该1行,跳过else和第一个后续行,并继续进行,就好像闭括号不存在一样。我认为这有点错误。最近我听到很多人抱怨这个问题(自5.3.7或其他版本以来)。将您的ini设置为E_STRICT错误报告并检查是否有任何差异。
看起来Besnik在我开始研究之前就已经找到了它(做得好,+1)。 @Lee:以下是避免此问题的方法:
RewriteEngine On
SetEnvIfNoCase X-Forwarded-For .+ proxy=yes
SetEnvIfNoCase X-moz prefetch no_access=yes

# block pre-fetch requests with X-moz headers
RewriteCond %{ENV:no_access} yes
RewriteRule .* - [F,L]

在这里找到了这个


if(array_key_exists("thumb", $_GET))
{
    $img = base64_decode($row["tNail"]);
}
else
{
    $img = base64_decode($row["image"]);
    $db->query("UPDATE gallery SET views = (views + 0.5) WHERE id = '$id'");
}

如果任意一个分支缺少花括号,那可能会导致问题。同样地,如果if语句没有被包含在代码块中,那么else语句后面的第一条语句(直到第一个分号)将被解释为一个分支。在你的情况下,这是对变量$ img的赋值。数据库插入将在两种情况下调用。
您可以使用__halt_compiler检查是否执行了db查询:
if(array_key_exists("thumb", $_GET))
{
    $img = base64_decode($row["tNail"]);
}
else
{
    $img = base64_decode($row["image"]);
    if ($db->query("UPDATE gallery SET views = (views + 0.5) WHERE id = '$id'"))
    {
        if (array_key_exists("thumb", $_GET))
        {//this should be an impossible branch
            __halt_compiler();//or exit || throw if this is a (member) function
        }
    }
}
__halt_compiler() 函数只能在全局命名空间中使用,且仅影响当前正在执行的脚本。如果该脚本是 - 例如 - index.php 的包含文件,则 index.php 将继续执行,但包含文件将停止执行。请参阅 文档 以获取更全面的解释。所有这些都无法帮助您在解决此问题时避免此问题,因此我建议您现在使用这个丑陋的解决方法:
if(array_key_exists("thumb", $_GET))
{
    $img = base64_decode($row["tNail"]);
    $db = null;//disconnect
}
else
{
    $img = base64_decode($row["image"]);
    if ($db !== null)
    {//or $db instanceof PDO (or whatever object you're using)
     //or try{$db->query('UPDATE...');}catch(Exception $e){}//<- suppress exception
        $db->query("UPDATE gallery SET views = (views + 0.5) WHERE id = '$id'");
    }
}

请告诉我上述哪种方法有效,或者您是否仍然遇到相同的问题。我认为这是一个非常有趣的案例 :)

嗯,我改变了错误报告,甚至为if语句添加了常规的大括号,但什么也没改变。当我注释掉print结构时,它会递增,非常奇怪。 - Lee
确实非常好奇,工作完成后我会看一下。很难理解这里发生了什么...总是有趣的去解决它! - Elias Van Ootegem
@李:如果重写规则有效,请告诉我,它可能会在某一天派上用场。 - Elias Van Ootegem
谢谢您。mod_rewrite.so已经启用,但是当我创建.htaccess并将其放置在站点的HTML文件夹中时,似乎没有任何变化。我没有.htaccess的经验。只需将其粘贴到空的.htaccess文件中是否足够? - Lee
@Lee:你已经完成了.htaccess的mod rewrite并且也改变了分支吗?为了清晰起见,我会更新我的答案来反映我认为你在这一点上拥有的if-else分支。 - Elias Van Ootegem
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接