强制下载,从php文件中

5
我正在尝试下载一个约1.3GB大小的.mp4文件。 我正在使用以下工具:
<?php 
$path = "video.mp4";
header('Accept-Ranges: bytes');  // For download resume
header('Cache-Control: must-revalidate, post-check=0, pre-check=0' );
header('Content-Description: File Transfer' );
header('Content-Disposition: attachment; filename="'.basename( $path ).'"' );
header('Content-Length: ' . filesize($path));  // File size
header('Content-Transfer-Encoding: binary');  // For Gecko browsers mainly
header('Content-Type: application/octet-stream' );
header('Expires: 0' );
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime($path)) . ' GMT');
header('Pragma: no-cache' );
ob_clean();
flush();
readfile($path);

我打开我的php文件,然后火狐浏览器弹出了“想要保存”菜单。文件大小看起来正确。

我按下另存为到我的桌面。最终下载的文件大小为随机大小,约为400MB(330,463和440)。

响应头如下:

Connection: Keep-Alive
Content-Disposition:    attachment; filename="//www.frederikspang.dk/elevgallavideo.mp4"
Content-Length: 1422778850
Content-Type:   video/mp4
Date:   Sun, 30 Jun 2013 22:12:30 GMT
Keep-Alive: timeout=10, max=50
Pragma: public
Server: Apache
content-transfer-encoding:  binary

1
你可能会遇到执行时间限制。下载是否总是在相同的时间段后停止? - user1864610
可恢复的文件下载需要比这更多的代码; 因此,您应该删除“Accept-Ranges”。请参见:https://dev59.com/o3VC5IYBdhLWcg3w4VRz - Rob W
我在脚本顶部加入了 set_time_limit(0)。但没有任何结果,它会在一段时间后停止,无法完成。 - Frederik Spang
@FrederikSpang:我非常确定这与脚本执行时间无关。当脚本到达readfile行时,据我所知,执行已经完成了。 - BudwiseЯ
你是否查看了已下载文件的末尾(或错误日志)以检查错误信息?导致文件仅部分下载的几乎所有问题都会抛出 PHP 错误。针对任何问题,我通常会打开错误报告功能并将 E_ALL 和 display_errors 都设置为开启状态(ini_set('display_errors', 'on'); error_reporting(E_ALL);),然后查看末尾是否存在错误(很可能会有)。 - Chelsea Urquhart
显示剩余10条评论
2个回答

2

这很困难 - 大多数php配置在30秒后会失败。如果您拥有php.ini,可以将其更改为更长的限制。但是 - 那真的值得吗?我的意思是 - 文件可能会变得更大或网络速度变慢 - 再次超时。

这就是下载器存在的原因 - 以较小的块下载大文件,Half CrazedTHIS答案中向您展示了代码(这不仅考虑了客户端协商传输的一种方式 - 但仍然是一个好的起点)。

例如,Mega.co.nz使用新的html5功能。在浏览器中使用块下载文件,在用户上连接文件,然后从浏览器磁盘空间“下载”它。它可以恢复文件,暂停文件等等。(抱歉 - 没有代码,因为它会相当庞大,包括多种语言(php,js))。

PS:将您的readfile($path);更改为:

$handle=fopen($path, 'rb');
while (!feof($handle))
{
    echo fread($handle, 8192);
    flush();
}
fclose($handle);

这不会将整个文件加载到内存中,而是每次只加载8KiB的部分,然后发送给用户。


尽管这是非常正确的 - 这是Apache2的Keep-Alive,不够高。 (刚刚看到这个问题,想给你一个答案) - Frederik Spang
抱歉回复晚了,但保持连接是用于其他原因的...大多数服务器将在60秒后终止php - 即使它仍在发送文件(也会被终止)。而且,解除这个限制通常不明智。 - Seti

0
   <?php
$filename = "theDownloadedFileIsCalledThis.mp4";
$myFile = "/absolute/path/to/my/file.mp4";

// Add bellow code for mime type
$ext=strtolower(substr($fl,strrpos($myFile,".")));
$mime_types = array(
            '.txt' => 'text/plain',
            '.htm' => 'text/html',
            '.html' => 'text/html',
            '.php' => 'text/html',
            '.css' => 'text/css',
            '.js' => 'application/javascript',
            '.json' => 'application/json',
            '.xml' => 'application/xml',
            '.swf' => 'application/x-shockwave-flash',
            '.flv' => 'video/x-flv',

            // images
            '.png' => 'image/png',
            '.jpe' => 'image/jpeg',
            '.jpeg' => 'image/jpeg',
            '.jpg' => 'image/jpeg',
            '.gif' => 'image/gif',
            '.bmp' => 'image/bmp',
            '.ico' => 'image/vnd.microsoft.icon',
            '.tiff' => 'image/tiff',
            '.tif' => 'image/tiff',
            '.svg' => 'image/svg+xml',
            '.svgz' => 'image/svg+xml',

            // video
            '.3gp' => 'video/3gpp',
            '.3g2' => 'video/3g2',
            '.avi' => 'video/avi',
            '.mp4' => 'video/mp4',
            '.asf' => 'video/asf',
            '.mov' => 'video/quicktime',
        );

if (array_key_exists($ext, $mime_types)){
   $mm_type=$mime_types[$ext];
}else{
   $mm_type="application/octet-stream";
}
$mm_type="application/octet-stream";

header("Cache-Control: public, must-revalidate"); // Avoid this line
header("Pragma: public"); // Add this line
header("Pragma: hack"); // Avoid this line
header("Content-Type: " . $mm_type);
header("Content-Length: " .(string)(filesize($myFile)) ); // Avoid this line
header('Content-Disposition: attachment; filename="'.$filename.'"');
header('Content-Length: ' . filesize($myFile)); // Add this line
header("Content-Transfer-Encoding: binary\n");
ob_clean(); // Add this line

readfile($myFile);

?>

1
非常不清楚的答案。文档记录得不好。请详细说明“避免”和“添加”,以及原因。 - Frederik Spang
MIME类型的参考是有用的,头声明也很有意义。"避免"显然意味着"不要使用",而"添加"则表示"添加"。他本可以提供更好的解释来说明为什么。 - zgr024
如果你最终要设置 $mm_type="application/octet-stream" 的话,那所有这些 MIME 类型的垃圾又有什么用呢? - Wes Johnson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接