使用 PHP 脚本提供图像与直接加载图像

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1353850/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 02:09:22  来源:igfitidea点击:

Serve image with PHP script vs direct loading an image

php

提问by Michel

I want to monitor how often some external images are loaded. So my idea is instead of giving a uri directly like this:

我想监控一些外部图像的加载频率。所以我的想法不是像这样直接给出一个uri:

www.site.com/image1.jpg

I can create a PHP script which reads the image, so I built a PHP file and my HTML would look like this:

我可以创建一个读取图像的 PHP 脚本,所以我构建了一个 PHP 文件,我的 HTML 将如下所示:

<img src="www.site.com/serveImage.php?img=image1.jpg">

but I don't know how to read the image from disk and return it. Would I return a byte array or set the content type?

但我不知道如何从磁盘读取图像并将其返回。我会返回一个字节数组还是设置内容类型?

Kind regards, Michel

亲切的问候,米歇尔

采纳答案by mck89

You must set the content type:

您必须设置内容类型:

header("Content-type: image/jpeg");

Then you load the image and output it like this:

然后你加载图像并像这样输出它:

$image=imagecreatefromjpeg($_GET['img']);
imagejpeg($image);

回答by OIS

Sending images through a script is nice for other things like resizing and caching on demand.

通过脚本发送图像对于其他事情来说是很好的,比如按需调整大小和缓存。

As answered by Pascal MARTIN the function readfileand these headers are the requirements:

正如 Pascal MARTIN 所回答的,函数readfile和这些标题是要求:

  • Content-Type
    • The mime type of this content
    • Example: header('Content-Type: image/gif');
    • See the function mime_content_type
    • Types
      • image/gif
      • image/jpeg
      • image/png
  • 内容类型
    • 此内容的 MIME 类型
    • 例子: header('Content-Type: image/gif');
    • 查看功能 mime_content_type
    • 类型
      • image/gif
      • image/jpeg
      • image/png

But beside the obvious content-type you should also look at other headers such as:

但是除了明显的内容类型之外,您还应该查看其他标题,例如:

  • Content-Length
    • The length of the response body in octets (8-bit bytes)
    • Example: header('Content-Length: 348');
    • See the function filesize
    • Allows the connectio to be better used.
  • Last-Modified
    • The last modified date for the requested object, in RFC 2822 format
    • Example: header('Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT');
    • See the function filemtimeand dateto format it into the required RFC 2822 format
      • Example: header('Last-Modified: '.date(DATE_RFC2822, filemtime($filename)));
    • You can exit the script after sending a 304 if the file modified time is the same.
  • status code
    • Example: header("HTTP/1.1 304 Not Modified");
    • you can exit now and not send the image one more time
  • 内容长度
    • 以八位字节为单位的响应正文的长度(8 位字节)
    • 例子: header('Content-Length: 348');
    • 查看功能 filesize
    • 允许更好地使用连接。
  • 最后修改
    • 请求对象的最后修改日期,采用 RFC 2822 格式
    • 例子: header('Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT');
    • 查看函数filemtime并将date其格式化为所需的 RFC 2822 格式
      • 例子: header('Last-Modified: '.date(DATE_RFC2822, filemtime($filename)));
    • 如果文件修改时间相同,您可以在发送 304 后退出脚本。
  • 状态码
    • 例子: header("HTTP/1.1 304 Not Modified");
    • 您现在可以退出并且不再发送图像

For last modified time, look for this in $_SERVER

对于上次修改时间,请在 $_SERVER

  • If-Modified-Since
    • Allows a 304 Not Modified to be returned if content is unchanged
    • Example: If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT
    • Is in $_SERVERwith the key http_if_modified_since
  • If-Modified-Since
    • 如果内容未更改,则允许返回 304 Not Modified
    • 例子: If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT
    • $_SERVER用钥匙http_if_modified_since

List of HTTP header responses

HTTP 标头响应列表

回答by Pascal MARTIN

To achieve something like this, your script will need to :

要实现这样的目标,您的脚本需要:

  • send the right headers, which depend on the type of the image : image/gif, image/png, image/jpeg, ...
  • send the data of the image
  • making sure nothingelse is sent (no white space, no nothing)
  • 发送正确的标题,这取决于图像的类型:image/gif、image/png、image/jpeg、...
  • 发送图像数据
  • 确保发送任何其他内容(没有空格,没有任何内容)

This is done with the headerfunction, with some code like this :

这是通过header函数完成的,代码如下:

header("Content-type: image/gif");

Or

或者

header("Content-type: image/jpeg");

or whatever, depending on the type of the image.

或者其他什么,取决于图像的类型。


To send the data of the image, you can use the readfilefunction :


要发送图像的数据,您可以使用以下readfile功能:

Reads a file and writes it to the output buffer.

读取文件并将其写入输出缓冲区。

This way, in one function, you both read the file, and output its content.

这样,在一个函数中,您既读取文件,又输出其内容。


As a sidenote :


作为旁注:

  • you must put some security in place, to ensure users can't request anything they want via your script : you must make sure it only serves images, from the directory you expect ; nothing like serveImage.php?file=/etc/passwdshould be OK, for instance.
  • If you're just willing to get the number of times a file was loaded each day, parsing Apache's log file might be a good idea (via a batch run by cron each day at 00:05, that parses the log of the day before, for instance) ; you won't have real-time statistics, but it will require less resources on your server (no PHP to serve static files)
  • 您必须采取一些安全措施,以确保用户无法通过您的脚本请求他们想要的任何内容:您必须确保它只提供您期望的目录中的图像;例如serveImage.php?file=/etc/passwd,没有什么应该可以的。
  • 如果您只想获取每天加载文件的次数,解析 Apache 的日志文件可能是一个好主意(通过 cron 每天在 00:05 运行的批处理,解析前一天的日志, 例如) ; 你不会有实时统计数据,但它会在你的服务器上需要更少的资源(没有 PHP 来提供静态文件)

回答by J. Bruni

I use the "passthru" function to call "cat" command, like this:

我使用“passthru”函数调用“cat”命令,如下所示:

header('Content-type: image/jpeg');
passthru('cat /path/to/image/file.jpg');

Works on Linux. Saves resources.

适用于 Linux。节省资源。

回答by Samer Ata

Instead of changing the direct image url in the HTML, you can put a line in the Apache configuration or .htaccessto rewrite all the requests of images in a directory to a php script. Then in that script you can make use of the request headers and the $_serverarray to process the request and serve the file.

您可以在 Apache 配置中添加一行,或者.htaccess将目录中的所有图像请求重写为 php 脚本,而不是更改 HTML 中的直接图像 url 。然后在该脚本中,您可以使用请求标头和$_server数组来处理请求并提供文件。

First in your .htaccess:

首先在您的 .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]

The script serve.phpmust be in the same directory as .htaccess. You will probably write something like this:

脚本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
*/
?>

(This script can be improved!)

(这个脚本可以改进!)

回答by nickf

You're probably better off examining your server access logs for this. Running all images through php might put a bit of load on your server.

您最好为此检查服务器访问日志。通过 php 运行所有图像可能会给您的服务器带来一些负载。

回答by dlchambers

Also, if you want to the user to see a real filename instead of your scriptname when the user RMC's on the image and selects "Save As", you'll need to also set this header:

此外,如果您希望用户在用户 RMC 位于图像上并选择“另存为”时看到真实的文件名而不是您的脚本名,您还需要设置此标题:

header('Content-Disposition: filename=$filename');

回答by htmlcoderexe

I serve my images with readfile as well, but I have gone the extra mile both for security and extra functionality.

我也使用 readfile 提供我的图像,但我在安全性和额外功能方面都付出了额外的努力。

I have a database set up which stores the image id, its dimensions and file extension. This also means that images need to be uploaded (allowing optional resizing), so I only use the system for content and not images needed for the website itself (like backgrounds or sprites).

我有一个数据库设置,用于存储图像 ID、其尺寸和文件扩展名。这也意味着需要上传图像(允许可选调整大小),所以我只使用系统的内容而不是网站本身所需的图像(如背景或精灵)。

It also does a very good job at making sure you can only request images.

它在确保您只能请求图像方面也做得很好。

So, for serving the simplified workflow would be like this (cannot post production code here):

因此,为简化工作流程提供服务是这样的(不能在此处发布生产代码):

1) get the ID of the requested image

1) 获取请求图片的ID

2) Look it up in the database

2)在数据库中查找

3) Throw headers based on the extension ("jpg" gets remapped to "jpeg" on upload)

3)根据扩展名抛出标题(“jpg”在上传时被重新映射到“jpeg”)

4) readfile("/images/$id.$extension");

4) readfile("/images/$id.$extension");

5) Optionally, protect /images/ dir so it cannot be indexed (not a problem in my own system as it maps URLS like /image/view/11to something like /index.php?module=image&action=view&id=11)

5) 或者,保护 /images/ 目录,使其无法被索引(在我自己的系统中这不是问题,因为它将/image/view/11 之类的URL 映射到/index.php?module=image&action=view&id=11 之类的内容