我的文件(126 MB大小,。exe)给了我一些问题。
我正在使用标准的laravel下载方法。
我尝试增加内存,但它仍然说我的内存不足,或者我下载了0 KB大小的文件。
该文档未提及任何有关大文件大小的内容。
我的代码是
ini_set("memory_limit","-1"); // Trying to see if this works
return Response::download($full_path);
我做错了什么?
- 编辑 -
继续发表Phill Sparks评论,这就是我所拥有的,它的作用。 它是Phill的组合加上一些来自php.net的组合。 不确定那里有什么遗失?
public static function big_download($path, $name = null, array $headers = array())
{
if (is_null($name)) $name = basename($path);
// Prepare the headers
$headers = array_merge(array(
'Content-Description' => 'File Transfer',
'Content-Type' => File::mime(File::extension($path)),
'Content-Transfer-Encoding' => 'binary',
'Expires' => 0,
'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0',
'Pragma' => 'public',
'Content-Length' => File::size($path),
), $headers);
$response = new Response('', 200, $headers);
$response->header('Content-Disposition', $response->disposition($name));
// If there's a session we should save it now
if (Config::get('session.driver') !== '')
{
Session::save();
}
// Below is from http://uk1.php.net/manual/en/function.fpassthru.php comments
session_write_close();
ob_end_clean();
$response->send_headers();
if ($file = fopen($path, 'rb')) {
while(!feof($file) and (connection_status()==0)) {
print(fread($file, 1024*8));
flush();
}
fclose($file);
}
// Finish off, like Laravel would
Event::fire('laravel.done', array($response));
$response->foundation->finish();
exit;
}
答案 0 :(得分:14)
这是因为Response::download()
在将文件提供给用户之前将文件加载到内存中。不可否认,这是框架中的一个缺陷,但大多数人并不试图通过框架提供大型文件。
解决方案1 - 将要下载的文件放在公共文件夹,静态域或cdn上 - 完全绕过Laravel。
可以理解的是,您可能会尝试通过登录来限制对下载的访问,在这种情况下,您需要制作自己的下载方法,这样的事情应该有效......
function sendFile($path, $name = null, array $headers = array())
{
if (is_null($name)) $name = basename($path);
// Prepare the headers
$headers = array_merge(array(
'Content-Description' => 'File Transfer',
'Content-Type' => File::mime(File::extension($path)),
'Content-Transfer-Encoding' => 'binary',
'Expires' => 0,
'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0',
'Pragma' => 'public',
'Content-Length' => File::size($path),
), $headers);
$response = new Response('', 200, $headers);
$response->header('Content-Disposition', $response->disposition($name));
// If there's a session we should save it now
if (Config::get('session.driver') !== '')
{
Session::save();
}
// Send the headers and the file
ob_end_clean();
$response->send_headers();
if ($fp = fread($path, 'rb')) {
while(!feof($fp) and (connection_status()==0)) {
print(fread($fp, 8192));
flush();
}
}
// Finish off, like Laravel would
Event::fire('laravel.done', array($response));
$response->foundation->finish();
exit;
}
此功能是Response::download()和Laravel的shutdown process的组合。我自己没有机会测试它,我没有安装Laravel 3。请告诉我它是否适合您。
PS:这个脚本唯一不需要处理的是cookie。不幸的是Response::cookies()函数受到保护。如果这成为问题,您可以从函数中提取代码并将其放在sendFile方法中。
PPS: 可能是输出缓冲的问题;如果问题在readfile() examples的PHP手册中查看,那么有一种方法可以在那里工作。
PPPS: 由于您使用的是二进制文件,因此您可能需要考虑将readfile()
替换为fpassthru()
编辑:无视PPS和PPPS,我已经更新了代码以使用fread + print代替,因为这似乎更稳定。
答案 1 :(得分:4)
您可以像这样使用Symfony \ Component \ HttpFoundation \ StreamedResponse:
$response = new StreamedResponse(
function() use ($filePath, $fileName) {
// Open output stream
if ($file = fopen($filePath, 'rb')) {
while(!feof($file) and (connection_status()==0)) {
print(fread($file, 1024*8));
flush();
}
fclose($file);
}
},
200,
[
'Content-Type' => 'application/octet-stream',
'Content-Disposition' => 'attachment; filename="' . $fileName . '"',
]);
return $response;
了解更多信息,请查看this
答案 2 :(得分:3)
我正在使用php.net here中所述的readfile_chunked()
自定义方法。对于Laravel 3,我已经扩展了响应方法:
将此文件添加为applications/libraries/response.php
<?php
class Response extends Laravel\Response {
//http://www.php.net/manual/en/function.readfile.php#54295
public static function readfile_chunked($filename,$retbytes=true) {
$chunksize = 1*(1024*1024); // how many bytes per chunk
$buffer = '';
$cnt =0;
// $handle = fopen($filename, 'rb');
$handle = fopen($filename, 'rb');
if ($handle === false) {
return false;
}
while (!feof($handle)) {
$buffer = fread($handle, $chunksize);
echo $buffer;
ob_flush();
flush();
if ($retbytes) {
$cnt += strlen($buffer);
}
}
$status = fclose($handle);
if ($retbytes && $status) {
return $cnt; // return num. bytes delivered like readfile() does.
}
return $status;
}
}
然后在application / config / application.php中注释掉这一行:
'Response' => 'Laravel\\Response',
示例代码:
//return Response::download(Config::get('myconfig.files_folder').$file->upload, $file->title);
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.$file->title);
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . File::size(Config::get('myconfig.files_folder').$file->upload));
ob_clean();
flush();
Response::readfile_chunked(Config::get('myconfig.files_folder').$file->upload);
exit;
到目前为止效果很好。
答案 3 :(得分:0)
你需要像这样增加你的memory_limit:
您需要使用ini_set函数增加memory_limit,例如ini_set('memory_limit','128M');
我希望这对你有用!
答案 4 :(得分:0)
2020 Laravel 7有更好的方法:
return response()->download($pathToFile);
当同一文件导致以前的解决方案出现问题时,我将其用于文件 398mb 没问题。
来自Laravel docs:“可以使用download方法生成响应,该响应迫使用户的浏览器在给定路径上下载文件。download方法接受文件名作为第二个参数。该方法将确定用户下载文件时看到的文件名。最后,您可以将HTTP标头数组作为该方法的第三个参数传递:
return response()->download($pathToFile);
return response()->download($pathToFile, $name, $headers);
return response()->download($pathToFile)->deleteFileAfterSend();
我们还流式传输了可能更适合的下载内容:Laravel docs