任何人都可以建议用于显示存储在文件系统中的图像的最佳PHP函数 - file_get_contents
或readfile
。我们正在显示存储在数据库中的图像,因此我们仍然需要通过PHP文件调用图像,而不能直接链接到文件系统。我见过人们推荐这两种功能,但我倾向于使用readfile
。非常感谢您的任何意见。
答案 0 :(得分:26)
如果您不需要操作图像(调整大小,添加水印,......)readfile()
将是最佳选择,因为它将文件直接写入输出缓冲区。 file_get_contents()
会将文件读入内存 - 需要大量内存和大文件。
答案 1 :(得分:9)
在编写这样的脚本时,请务必考虑缓存!
当在下一页访问时重新请求图像时,服务静态图像的Web服务器将与客户端进行协商,并且如果服务器确定客户端的图像的缓存副本仍然有效,则图像不会被转发。
由于天真的脚本没有进行这种协商,因此在每次页面请求时都会将图像重新传输到客户端,从而导致您的带宽超出必要的范围。
这有三种机制。我无法确切地告诉你如何编写最佳脚本,因为我以前从未必须这样做,我不确定不同的缓存头如何互操作以及HTTP版本,但我鼓励你研究这个进一步
我所知道的三种机制:
过期(HTTP / 1.0)
最简单的一个。此标题告诉客户端图像在给定的时刻之前肯定会有效。在此时间过去之前,客户端甚至不会对脚本执行请求,因此适当地设置此选项可以节省您(部分)服务器上的CPU周期以及Web应用程序中的图像加载延迟。
如何设置这完全取决于您的应用程序;你的图像变化很快或很少?如果图像在您发送到客户端的Expires时间之前发生更改,则客户端将看不到新图像。
示例:
header("Expires: " . gmdate('D, d-M-Y H:i:s \G\M\T', time() + 60)); // Valid for a minute
(注意:Expires似乎已被HTTP / 1.1中的 Cache-Control 取代)
If-Modified-Since (HTTP / 1.1)
HTTP / 1.1客户端可以发送此标头(如果它已有图像副本),并记录副本的确定时间。然后,您可以在数据库中确定当前版本的映像是在更早或更晚的时间更改的。如果客户端的版本仍然是正确的,只需发送“304 Not Modified”响应并退出(从而防止必须传输图像)。
示例:
$cache_time = parse_browsers_date_time_format($_SERVER["IF-MODIFIED-SINCE"]);
$actual_time = get_current_resource_time_from_db();
if ($actual_time <= $cache_time) {
header("HTTP/1.1 304 Not Modified");
die;
}
// ... Produce and output resource here
(注意:如果您还在原始回复中发送修改后的,客户可能只会实际发送 If-Modified-Since 。我不确定,为自己研究。)
ETag / If-None-Match (HTTP / 1.1)
此方法类似于 If-Modified-Since 协商,但不是时候使用图像的哈希来查看内容是否已更改。它的工作原理如下:服务器为图像计算一些哈希值,并在第一次在 ETag 标题中请求图像时发送此哈希值。
在后续请求中,服务器将在请求字段 If-None-Match 中发回回传。如果客户端的哈希值与图像的当前哈希值相同,则图像之间没有更改,脚本只需发送“304 Not Modified”即可。
由于ETags似乎实际上是用于防止带有副作用的客户端请求中的并发问题(即POST和PUT),并且因为计算哈希是一项代价高昂的操作,我认为If-Modified-Since方法将更适合大多数文件服务应用程序。
答案 2 :(得分:4)
我会做类似的事情:
header('Content-type: image/jpeg');
readfile('something.jpg');
readfile()似乎对此目的更有用,因为它读取文件并将其写入输出缓冲区,返回从文件读取的字节数,或者在错误时返回false。
答案 3 :(得分:0)
我应该使用fpassthru 使用fopen可以更精确地处理错误。
PS:请确保,文件系统的直接输出不容易受到攻击!
答案 4 :(得分:0)
file_get_contents()
是将文件内容读入字符串的首选方法。如果您的操作系统支持,它将使用内存映射技术来提高性能。但除此之外,我不知道什么是更好的。您可以尝试查看一个巨大的文件是否可以提高file_get_contents()
的性能,但我预计网络流量比服务器文件IO更受关注。 readfile()
用于通过content-type
传递header()
来下载文件。