9

我正在尝试向用户提供大型 zip 文件。当有 2 个并发连接时,服务器会耗尽内存 (RAM)。我将内存量从 300MB 增加到 4GB(Dreamhost VPS),然后它运行良好。

我需要允许超过 2 个并发连接。实际的 4GB 将允许 20 个并发连接(太糟糕了)。

好吧,我正在使用的当前代码需要两倍的内存,然后是实际的文件大小。这太糟糕了。我想要将文件“流式传输”给用户之类的东西。所以我会分配不超过提供给用户的块。

以下代码是我在 CodeIgniter(PHP 框架)中使用的代码:

ini_set('memory_limit', '300M'); // it was the maximum amount of memory from my server
set_time_limit(0); // to avoid the connection being terminated by the server when serving bad connection downloads
force_download("download.zip", file_get_contents("../downloads/big_file_80M.zip"));exit;

force_download函数如下(CodeIgniter默认辅助函数):

function force_download($filename = '', $data = '')
{
    if ($filename == '' OR $data == '')
    {
        return FALSE;
    }

    // Try to determine if the filename includes a file extension.
    // We need it in order to set the MIME type
    if (FALSE === strpos($filename, '.'))
    {
        return FALSE;
    }

    // Grab the file extension
    $x = explode('.', $filename);
    $extension = end($x);

    // Load the mime types
    @include(APPPATH.'config/mimes'.EXT);

    // Set a default mime if we can't find it
    if ( ! isset($mimes[$extension]))
    {
        $mime = 'application/octet-stream';
    }
    else
    {
        $mime = (is_array($mimes[$extension])) ? $mimes[$extension][0] : $mimes[$extension];
    }

    // Generate the server headers
    if (strpos($_SERVER['HTTP_USER_AGENT'], "MSIE") !== FALSE)
    {
        header('Content-Type: "'.$mime.'"');
        header('Content-Disposition: attachment; filename="'.$filename.'"');
        header('Expires: 0');
        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
        header("Content-Transfer-Encoding: binary");
        header('Pragma: public');
        header("Content-Length: ".strlen($data));
    }
    else
    {
        header('Content-Type: "'.$mime.'"');
        header('Content-Disposition: attachment; filename="'.$filename.'"');
        header("Content-Transfer-Encoding: binary");
        header('Expires: 0');
        header('Pragma: no-cache');
        header("Content-Length: ".strlen($data));
    }

    exit($data);
}

我尝试了一些在 Google 中找到的基于块的代码,但文件总是被损坏。可能是因为代码不好。

有人可以帮我吗?

4

6 回答 6

3

这个线程中有一些想法。我不知道 readfile() 方法是否会节省内存,但听起来很有希望。

于 2011-06-01T02:33:06.233 回答
3

您是通过 PHP 发送此文件的内容 ($data) 吗?

如果是这样,处理此问题的每个 Apache 进程最终都会增长到该文件的大小,因为该数据将被缓存。

您唯一的解决方案是不通过 PHP 发送文件内容/数据,而只是将用户重定向到文件系统上的下载 URL。

使用生成的唯一符号链接或隐藏位置。

于 2011-06-01T12:38:28.217 回答
1

我搜索了太多的脚本和建议,但对我的 400MB PDF 文件没有任何帮助。我最终使用了 mod_rewrite,这是它工作得很好的解决方案https://unix.stackexchange.com/questions/88874/control-apache-referrer-to-restrict-downloads-in-htaccess-file 该代码只允许从您指定并禁止直接下载的referrer

 RewriteEngine On
RewriteCond %{HTTP_REFERER} !^http://yourdomain.com/.* [NC]
RewriteRule .* - [F]
于 2019-12-22T22:43:25.747 回答
0

您不能将 $data 与其中的整个文件数据一起使用。尝试传递给这个函数,而不是文件的内容,只是它的路径。接下来发送所有标题一次,然后使用 fread() 读取该文件的一部分,回显该块,调用 flush() 并重复。如果同时发送任何其他标头,则最终传输将被破坏。

于 2011-06-01T02:38:34.003 回答
0

将大文件符号链接到您的文档根目录(假设它不是授权的唯一文件),然后让 Apache 处理它。(这样你也可以接受字节范围)

于 2011-06-01T02:42:07.060 回答
0

添加你的ini_set之前SESSION_START();

于 2011-07-07T11:50:00.573 回答