我正在尝试向用户提供大型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中找到的基于块的代码,但文件总是被破坏了。可能是因为代码不好。
谁能帮助我?
在this thread有一些想法。我不知道readfile()方法是否会节省内存,但听起来很有希望。
你是通过PHP发送这个文件的内容($ data)?
如果是这样,处理此问题的每个Apache进程最终都会增长到此文件的大小,因为该数据将被缓存。
您唯一的解决方案是不通过PHP发送文件内容/数据,只需将用户重定向到文件系统上的下载URL。
使用生成的唯一符号链接或隐藏位置。
您不能将$ data与其中的整个文件数据一起使用。尝试传递给这个函数而不是文件的内容只有它的路径。接下来发送所有标题一次,之后使用fread()读取该文件的一部分,回显该块,调用flush()并重复。如果在此期间发送任何其他标头,则最终传输将被破坏。
将大文件符号链接到您的文档根目录(假设它不是仅授权的文件),然后让Apache处理它。 (这样你也可以接受字节范围)
在ini_set
之前添加你的SESSION_START();