在浏览器中使用URL参数和XSendFile缓存图像

8
我正在尝试建立一个网站,使用户只能访问自己的图像和音频文件。为此,我使用URL中的变量,例如:
 <img src="/public/get_file.php?type=image&file=pic001.png" />

在前端,我使用React JS(不确定是否与此问题有关)。但是在后端,PHP脚本将验证用户是否已登录(仅通过检查一个简单的SESSION变量),并在用户数据目录中查找该文件(基于其SESSION变量中的用户ID)。如果存在,则使用XSendFile将其返回给用户。
我遇到的问题是,每次用户尝试访问文件时,加载之前都会有一些延迟。这告诉我它们可能没有被浏览器缓存。
为什么文件没有被缓存?是与URL参数或PHP / XSendFile的使用有关吗?
我应该怎么做才能让我的文件(图像/音频)被缓存?
更新:
按照要求,这是我的get_file.php文件:
<?php

        session_start();

        if (empty($_SESSION['auth']['id'])){
                exit;
        }

        require_once("../../api_modules/settings.php");

        $developer_id = $_SESSION['auth']['id'];

        $type = preg_replace('/[^-a-zA-Z0-9_]/', '', $_GET['type']);
        $file = preg_replace('/[^-a-zA-Z0-9_\.]/', '', $_GET['file']);

        $file = preg_replace('/[\.]{2,}/', '', $file);

        $project_id = preg_replace('/[^0-9]/', '', $_GET['project_id']);
        $developer_id = preg_replace('/[^0-9]/', '', $developer_id);

        if ($type == "image")
                $file = FILEPATH ."files/$developer_id/$project_id/images/thumbs/88x88/$file";
        else if ($type == "full_image")
                $file = FILEPATH ."files/$developer_id/$project_id/images/originals/$file";
        else if ($type == "audio"){
                $file = FILEPATH ."files/$developer_id/$project_id/audio/$file";
        }
        else {
                exit;
        }


        header("X-Sendfile: $file");
        header("Content-type: application/octet-stream");
        header('Content-Disposition: attachment; filename="' . basename($file) . '"');

@jsdeveloper 虽然我经常使用控制台,但我不太习惯使用网络选项卡(响应)部分。然而,我确实看到了我的请求,但响应显示:“请求没有可用的响应数据”。 - kojow7
仅供澄清,您希望浏览器能够缓存图像吗? - Vidal
@TomaszKajtoch,我不完全理解你的问题。我的问题标记为Apache。你还在寻找其他东西吗? - kojow7
@Vidal 是的,目前如果我进入一个页面,它会加载图片,如果我离开到另一个页面,然后再回到第一个页面,它必须重新加载图片。 - kojow7
你能发一下你的 get_file.php 代码吗? - Alex
显示剩余2条评论
4个回答

0

0
你是如何生成这些图片的?你有公共本地目录吗?还是通过CDN(远程)加载?
或许这篇文章可以帮到您:PHP生成图片的缓存方法

图片存储于同一服务器上的目录中。每个用户都有自己的用户目录,以其用户ID标记。 - kojow7
好的,使用本地图像来管理缓存会更容易。您可以尝试链接上的解决方案,看看它是否有效。但是如果有人直接访问这些图像会发生什么? - dm777
由于图片不在网络可访问的文件夹中,因此他们无法直接访问图片。 - kojow7

0
尝试使用这种类型的标题(PHP代码),例如header('Cache-Control: must-revalidate');,以下载私有文件。
$mime_types = array(
        'pdf' => 'application/pdf',
        'txt' => 'text/plain',
        'html' => 'text/html',
        'exe' => 'application/octet-stream',
        'zip' => 'application/zip',
        'doc' => 'application/msword',
        'xls' => 'application/vnd.ms-excel',
        'ppt' => 'application/vnd.ms-powerpoint',
        'gif' => 'image/gif',
        'png' => 'image/png',
        'jpeg' => 'image/jpg',
        'jpg' => 'image/jpg',
        'php' => 'text/plain'
        );

if ($mimeType == '') {
    $file_ext = strtolower(substr(strrchr($file_path.$file_name_gen, '.'), 1));
    if(array_key_exists($file_ext, $mime_types)) $mime_type = $mime_types[$file_ext];
    else $mime_type = 'application/force-download';
    }


ob_start();
header('Content-Disposition: attachment; filename="' . $file_name_gen . '"');
header('Content-Type: '.$mime_type);
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file_path.$file_name));
ob_clean();

flush();
readfile($file_path.$file_name);

对于您的解决方案,请使用最佳选项使用缓存控制

Cache-Control: max-age=<seconds>
Cache-Control: max-stale[=<seconds>]
Cache-Control: min-fresh=<seconds>
Cache-Control: no-cache 
Cache-Control: no-store
Cache-Control: no-transform
Cache-Control: only-if-cached

顺便提一下,$file_name_gen 是服务器上真实的文件名,而 $file_name 则是用户为了更好的文件隐私所看到的文件名。


Cache-Control: private 用于避免被代理服务器缓存。 - UjinT34

0

我认为这是一个重复的问题。

但既然有悬赏,让我们尝试将您的代码与建议的解决方案结合起来适应它。

您需要检查Apache是否已启用并正常工作mod_expires和mod_headers。

然后,在开始真正输出之前,请检查文件是否已修改:

...
$last_modified_time = filemtime($file); 
$etag = md5_file($file);
// always send headers
header("Last-Modified: ".gmdate("D, d M Y H:i:s", $last_modified_time)." GMT"); 
header("Etag: $etag"); 
// exit if not modified
if (@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $last_modified_time || 
    @trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag) { 
    header("HTTP/1.1 304 Not Modified"); 
    exit; 
} else {
    header("X-Sendfile: $file");
    header("Content-type: application/octet-stream");
    header('Content-Disposition: attachment; filename="' . basename($file) . '"');
}

我不确定我完全理解,但是Web浏览器是否会请求文件,但如果收到304消息,则取消其请求并使用其缓存? - kojow7
是的,这就是浏览器如何重用缓存的HTTP响应。 - Alex
可能明天没有时间完全测试这个,但是对我来说这似乎是最好的答案。 - kojow7

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