【问题标题】:How to enable X-Accel-Redirect如何启用 X-Accel-Redirect
【发布时间】:2022-02-19 01:39:11
【问题描述】:

我需要将服务器端tmpfile()创建的文件发送到浏览器。

我正在使用两个不同的 Docker 容器:phpnginx(docker compose)。

我的 nginx 配置文件(Docker 容器nginx):

server {
    listen 80 default_server;
 
    index index.php index.html index.htm;
 
    root /app;

    location ~ [^/]\.php(/|$) {
        autoindex on;
        autoindex_exact_size on;
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
 
    error_log  stderr warn;
    access_log  /dev/stdout main;
}

我的 PHP 代码(Docker 容器php):

<?php

$file = tmpfile();
fwrite($file, "hello world");

$metaData = stream_get_meta_data($file);
$fileName = $metaData['uri'];

header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Content-type: text/plain');
header('Content-Length: ' . filesize($fileName));
header('X-Sendfile: ' . $fileName);

fclose($file); 

但是浏览器只得到一个空页面而不是下载弹出窗口。

【问题讨论】:

  • 您无法使用tmpfile() 来完成此操作。您是否需要详细回答为什么以及可以建议什么?
  • @IvanShatsky 为什么 tmpfile() 不可能?还有什么替代方案?

标签: php docker nginx docker-compose


【解决方案1】:

好的,让我们从头开始。

  1. 很明显,nginx 需要直接访问将通过X-Accel-Redirect 标头提供的文件。这意味着您需要在nginxphp 容器之间建立某种共享卷。通常这不是问题(例如参见How To Share Data between Docker Containers 文章)。但是tmpfile() 会在临时目录(/tmp 或类似的东西)中创建一个临时文件,并使其成为容器之间的共享卷可能非常棘手。

  2. 即使您设法解决了前一部分,在fclose($file); 行之后,临时文件也会被关闭并删除。这就是为什么尝试同时使用tmpfile()X-Accel-Redirect 是一个坏主意的主要原因。好吧,您可以尝试刷新输出缓冲区并等待一段时间,让 nginx 有机会在脚本关闭并删除文件之前读取文件,但我不确定它是否能正常工作,也不能似乎也是一个很好的解决方案。

你能做些什么呢?

首先,您根本不需要坚持使用X-Accel-Redirect。当您想使用 nginx 提供已存在的文件时,它可以为您带来显着的性能优势。但是您将首先创建该文件,使用 PHP 将一些数据写入磁盘,然后使用 nginx 从磁盘读取它。您可以直接将该数据直接写入STDOUT

这种方法的缺点是您事先不知道数据量,并且无法设置正确的 Content-Length 标头,从而使您的数据通过分块编码的 HTTP 流提供服务。如果您不想那样做,您可以使用输出缓冲ob_... 函数。使用ob_start() 开始缓冲,然后在将所有数据写入缓冲区后使用ob_get_contents() 获取其内容,使用ob_get_length() 获取数据大小,然后使用ob_end_clean() 关闭输出缓冲区。现在您将能够在将数据发送到STDOUT 之前正确设置Content-Length 标头(或者如果出现问题,则返回正确的错误代码)。

作为最后的手段,如果您仍然想完全使用tmpfile(),您可以在使用readfile() 函数关闭/删除它之前将其内容输出到STDOUT

【讨论】:

    猜你喜欢
    • 2018-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    • 1970-01-01
    • 2011-04-30
    • 1970-01-01
    相关资源
    最近更新 更多