【问题标题】:Caching images in browser with URL parameters and XSendFile使用 URL 参数和 XSendFile 在浏览器中缓存图像
【发布时间】:2019-03-10 18:17:25
【问题描述】:

我正在尝试建立一个网站,以便用户只能访问他们自己的图像和音频文件。为此,我在 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) . '"');

【问题讨论】:

  • 你能发布来自 Chrome 开发工具网络标签的图像请求的响应吗?
  • @jsdeveloper 虽然我经常使用控制台,但我不太习惯使用网络选项卡(响应)部分。但是,我确实看到了我的请求,但响应显示:“该请求没有可用的响应数据”。
  • 只是为了澄清,您希望浏览器能够缓存图像吗?
  • @kojow7 你用的是哪个HTTP服务器?
  • @TomaszKajtoch 我不完全理解你的问题。我的问题被标记为 Apache。你还有什么要找的吗?

标签: php reactjs apache browser-cache x-sendfile


【解决方案1】:

我认为这是duplicate

但既然有赏金,让我们尝试将您的代码与那里建议的解决方案一起调整。

您需要检查 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) . '"');
}

【讨论】:

  • 我不确定我是否完全理解,但是网络浏览器是否请求该文件,但如果它收到 304 消息,它会取消其请求并改为使用其缓存?
  • 是的。这就是浏览器如何重用缓存的 http 响应
  • 明天可能没有时间对此进行全面测试,但这对我来说似乎是最好的答案。
【解决方案2】:

尝试使用这种标头(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 避免被代理服务器缓存。
【解决方案3】:

据记录,这主要是因为session.cache_limiter 的默认值是nocache

请参阅http://php.net/manual/en/function.session-cache-limiter.php,了解如何设置适合您需要的不同值。

【讨论】:

    【解决方案4】:

    如何生成图像?您是否有一个公共的本地目录或者它是由 CDN(远程)加载的?

    也许可以帮上忙:HOW TO CACHE IMAGES GENERATED BY PHP

    【讨论】:

    • 图像存储在同一服务器上的目录中。每个用户都有自己的用户目录,由他们的用户 ID 标记。
    • 好的,使用本地图片管理缓存更容易。您可以尝试链接上的解决方案,看看它是否有效。但是,如果有人尝试直接访问图像会怎样?
    • 他们无法直接访问图像,因为它们不在网络可访问文件夹中。
    猜你喜欢
    • 2015-12-07
    • 2018-04-20
    • 2010-12-27
    • 2012-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-12
    • 1970-01-01
    相关资源
    最近更新 更多