【发布时间】:2009-05-26 08:54:42
【问题描述】:
任何人都可以建议显示存储在文件系统中的图像的最佳 PHP 函数是什么 - file_get_contents 或 readfile。我们正在从显示存储在数据库中的图像切换,因此我们仍然需要通过 PHP 文件调用图像并且不能直接链接到文件系统。我见过有人推荐这两种功能,但我倾向于使用readfile。非常感谢您对此的任何意见。
【问题讨论】:
任何人都可以建议显示存储在文件系统中的图像的最佳 PHP 函数是什么 - file_get_contents 或 readfile。我们正在从显示存储在数据库中的图像切换,因此我们仍然需要通过 PHP 文件调用图像并且不能直接链接到文件系统。我见过有人推荐这两种功能,但我倾向于使用readfile。非常感谢您对此的任何意见。
【问题讨论】:
如果您不需要处理图像(调整大小、添加水印...)readfile() 将是最佳选择,因为它将文件直接写入输出缓冲区。 file_get_contents() 会将文件读入内存 - 大文件需要大量内存。
【讨论】:
在编写这样的脚本时,一定要考虑到缓存!
提供静态图片的网络服务器会在下一次页面访问重新请求图片时与客户端进行协商,如果服务器确定客户端缓存的图片副本仍然有效,则图片不会重传。
由于原始脚本不进行此协商,因此图像将在每次页面请求时重新传输给客户端,从而花费您不必要的带宽。
为此有三种机制。我无法确切告诉您如何编写最佳脚本,因为我以前从未这样做过,而且我不确定不同的缓存标头如何互操作以及在什么 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
(注意:如果您在原始响应中还发送 Last-Modified,客户可能只会实际发送 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 方法将更适合大多数文件服务应用程序。
【讨论】:
header("HTTP/1.1 304 Not Modified"); 解决了它我不完全理解强制 Not Modified 的含义但图像在下次加载时加载速度更快,因此它必须工作。
我会这样做:
header('Content-type: image/jpeg');
readfile('something.jpg');
readfile() 似乎更适合此目的,因为它读取文件并将其写入输出缓冲区,返回从文件读取的字节数,或错误时返回 false。
【讨论】:
我应该使用fpassthru 使用 fopen 可以更准确地处理错误。
PS:请确保您从文件系统直接输出的内容不会受到攻击!
【讨论】:
file_get_contents() 是将文件内容读入字符串的首选方式。如果您的操作系统支持,它将使用内存映射技术来提高性能。但除此之外,我不知道什么更好。您可以尝试使用file_get_contents() 查看大文件是否能提供更好的性能,但我预计网络流量比服务器文件-IO 更令人担忧。而readfile() 用于通过在header() 中传递content-type 来下载文件。
【讨论】: