如何在不占用过多内存的情况下强制下载大文件?

问题描述

| 我正在尝试为用户提供大型zip文件。当有2个并发连接时,服务器内存不足(RAM)。我将内存量从300MB增加到4GB(Dreamhost VPS),然后工作正常。 我需要允许两个以上的并发连接。实际的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中找到的基于块的代码,但是该文件始终交付已损坏。可能是因为代码错误。 有人可以帮我吗?     

解决方法

        这个线程中有一些想法。我不知道readfile()方法是否可以节省内存,但这听起来很有希望。     ,        您要通过PHP发送此文件的内容($ data)吗? 如果是这样,每个处理此问题的Apache进程最终将增长到该文件的大小,因为该数据将被缓存。 您唯一的解决方案是不通过PHP发送文件内容/数据,而只是将用户重定向到文件系统上的下载URL。 使用生成的唯一符号链接或隐藏位置。     ,        我搜索了太多脚本和建议,但400MB PDF文件没有任何作用。我最终使用了mod_rewrite,这是它很好用的解决方案https://unix.stackexchange.com/questions/88874/control-apache-referrer-to-restrict-downloads-in-htaccess-file 该代码仅允许从您指定的引荐来源下载,并禁止直接下载
 RewriteEngine On
RewriteCond %{HTTP_REFERER} !^http://yourdomain.com/.* [NC]
RewriteRule .* - [F]
    ,        您不能将$ data与整个文件数据一起使用。尝试传递给此函数,而不是仅传递文件的路径。接下来发送一次所有标头,然后使用fread()读取该文件的一部分,回显该块,调用flush()并重复。如果在此期间发送其他任何报头,则最终传输将被破坏。     ,        将大文件符号链接到您的文档根目录(假定其不是唯一的授权文件),然后让Apache处理它。 (这样,您也可以接受字节范围)     ,        在
SESSION_START();
之前加
ini_set