我想要监测一些外部图片被加载的频率。因此,我的想法是不直接提供像这样的uri:
www.site.com/image1.jpg
我可以创建一个读取图像的PHP脚本,所以我编写了一个PHP文件,我的HTML如下:
<img src="www.site.com/serveImage.php?img=image1.jpg">
但我不知道如何从磁盘中读取图像并返回它。我应该返回一个字节数组还是设置内容类型?
敬礼, 米歇尔
我想要监测一些外部图片被加载的频率。因此,我的想法是不直接提供像这样的uri:
www.site.com/image1.jpg
我可以创建一个读取图像的PHP脚本,所以我编写了一个PHP文件,我的HTML如下:
<img src="www.site.com/serveImage.php?img=image1.jpg">
但我不知道如何从磁盘中读取图像并返回它。我应该返回一个字节数组还是设置内容类型?
敬礼, 米歇尔
readfile
和以下头信息是必要的:
header('Content-Type: image/gif');
mime_content_type
image/gif
image/jpeg
image/png
header('Content-Length: 348');
filesize
header("HTTP/1.1 304 Not Modified");
要查找上次修改时间,请在 $_SERVER
中寻找以下内容
If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT
http_if_modified_since
的形式在 $_SERVER
中要实现这样的效果,你的脚本需要:
使用header
函数来完成这些操作,像下面这样编写代码:
header("Content-type: image/gif");
或者
header("Content-type: image/jpeg");
根据图像类型,可以选择使用不同的方法。
要发送图像数据,您可以使用 readfile
函数:
读取文件并将其写入输出缓冲区。
这样,您就可以在一个函数中同时读取文件并输出其内容。
作为一则旁注:
serveImage.php?file=/etc/passwd
这样的请求是不可以接受的。我使用“passthru”函数调用“cat”命令,就像这样:
header('Content-type: image/jpeg');
passthru('cat /path/to/image/file.jpg');
适用于Linux。节省资源。
passthru
命令。 - Philipp你必须设置内容类型:
header("Content-type: image/jpeg");
然后你加载图像并像这样输出:
$image=imagecreatefromjpeg($_GET['img']);
imagejpeg($image);
与其在HTML中更改直接的图像URL,您可以在Apache配置或.htaccess
中添加一行来将目录中所有图像的请求重写到一个PHP脚本中。然后,在该脚本中,您可以利用请求头和$_server
数组来处理请求并提供文件。
首先在您的.htaccess
文件中:
RewriteRule ^(.*)\.jpg$ serve.php [NC]
RewriteRule ^(.*)\.jpeg$ serve.php [NC]
RewriteRule ^(.*)\.png$ serve.php [NC]
RewriteRule ^(.*)\.gif$ serve.php [NC]
RewriteRule ^(.*)\.bmp$ serve.php [NC]
serve.php
必须与.htaccess
位于同一目录中。你可能会写出类似以下的代码:<?php
$filepath=$_SERVER['REQUEST_URI'];
$filepath='.'.$filepath;
if (file_exists($filepath))
{
touch($filepath,filemtime($filepath),time()); // this will just record the time of access in file inode. you can write your own code to do whatever
$path_parts=pathinfo($filepath);
switch(strtolower($path_parts['extension']))
{
case "gif":
header("Content-type: image/gif");
break;
case "jpg":
case "jpeg":
header("Content-type: image/jpeg");
break;
case "png":
header("Content-type: image/png");
break;
case "bmp":
header("Content-type: image/bmp");
break;
}
header("Accept-Ranges: bytes");
header('Content-Length: ' . filesize($filepath));
header("Last-Modified: Fri, 03 Mar 2004 06:32:31 GMT");
readfile($filepath);
}
else
{
header( "HTTP/1.0 404 Not Found");
header("Content-type: image/jpeg");
header('Content-Length: ' . filesize("404_files.jpg"));
header("Accept-Ranges: bytes");
header("Last-Modified: Fri, 03 Mar 2004 06:32:31 GMT");
readfile("404_files.jpg");
}
/*
By Samer Mhana
www.dorar-aliraq.net
*/
?>
(这个脚本可以改进!)
header('Content-Disposition: filename=$filename');
我也使用readfile来提供图片服务,但为了安全和额外的功能,我做了更多的工作。
我建立了一个数据库,存储图片ID、尺寸和文件扩展名。这也意味着需要上传图片(允许可选的调整大小),因此我只将系统用于内容,而不是网站本身所需的图片(如背景或精灵)。
它还非常擅长确保您只能请求图像。
因此,提供简化的工作流程如下(无法在此处发布生产代码):
1)获取所请求图像的ID
2)在数据库中查找
3)基于扩展名抛出标头(上传时,“jpg”被重新映射为“jpeg”)
4)readfile("/images/$id.$extension");
5)可选地保护/images/目录,以使其无法被索引(在我的系统中不是问题,因为它将URL映射为类似于/index.php?module=image&action=view&id=11的/image/view/11)
您最好检查服务器访问日志以查找此问题。将所有图像都通过php运行可能会给您的服务器带来一些负载。
以上有很多好的答案,但是没有一个提供可以在您的PHP应用程序中使用的工作代码。我已经设置了我的代码,以便根据不同的标识符在数据库表中查找图像的名称。客户端从未设置要下载的文件名,因为这是一种安全风险。
一旦找到图像名称,我将其分解以获取扩展名。这很重要,以了解基于图像类型(即png、jpg、jpeg、gif等)提供什么类型的头文件。出于安全原因和将jpg转换为jpeg以获得正确的标题名称,我使用switch来执行此操作。我在我的代码中包含了一些额外的头文件,以确保文件不被缓存,需要重新验证,更改名称(否则它将是调用的脚本的名称),最后从服务器读取文件并传输它。
我喜欢这种方法,因为它从不公开目录或实际文件名。如果您正在尝试以安全方式执行此操作,请确保在运行脚本之前对用户进行身份验证。
$temp = explode('.', $image_filename);
$extension = end($temp); // jpg, jpeg, gif, png - add other flavors based off your use case
switch ($extension) {
case "jpg":
header('Content-type: image/jpeg');
break;
case "jpeg":
case "gif":
case "png":
header('Content-type: image/'.$extension);
break;
default:
die; // avoid security issues with prohibited extensions
}
header('Content-Disposition: filename=photo.'.$extension);
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
readfile('../SECURE_DIRECTORY/'.$image_filename);
PHP 8 允许您使用匹配功能,通过摆脱 switch 和丑陋的嵌套 case 进一步优化代码。