【问题标题】:How to download large files through PHP script [duplicate]如何通过PHP脚本下载大文件[重复]
【发布时间】:2011-09-25 13:10:44
【问题描述】:

使用 PHP,我正在尝试提供由于授权问题而不在 Web 可访问目录中的大文件(最大可能为 200MB)。目前,我使用readfile() 调用以及一些标头来提供文件,但似乎 PHP 在发送之前将其加载到内存中。我打算部署在共享主机服务器上,这不会让我使用太多内存或添加我自己的 Apache 模块,例如 X-Sendfile。

出于安全原因,我不能让我的文件位于 Web 可访问目录中。有人知道我可以在共享托管服务器上部署的内存密集型方法吗?

编辑:

if(/* My authorization here */) {
        $path = "/uploads/";
        $name = $row[0];           //This is a MySQL reference with the filename
        $fullname = $path . $name; //Create filename
        $fd = fopen($fullname, "rb");
        if ($fd) {
            $fsize = filesize($fullname);
            $path_parts = pathinfo($fullname);
            $ext = strtolower($path_parts["extension"]);
            switch ($ext) {
                case "pdf":
                header("Content-type: application/pdf");
                break;
                case "zip":
                header("Content-type: application/zip");
                break;
                default:
                header("Content-type: application/octet-stream");
                break;
            }
            header("Content-Disposition: attachment; filename=\"".$path_parts["basename"]."\"");
            header("Content-length: $fsize");
            header("Cache-control: private"); //use this to open files directly
            while(!feof($fd)) {
                $buffer = fread($fd, 1*(1024*1024));
                echo $buffer;
                ob_flush();
                flush();    //These two flush commands seem to have helped with performance
            }
        }
        else {
            echo "Error opening file";
        }
        fclose($fd);

【问题讨论】:

  • 不同意这是重复的,这家伙正试图通过 PHP 下载,而另一个问题是如何下载大文件.. 老实说不同的问题。

标签: php memory download


【解决方案1】:

如果您使用fopen 和fread 而不是readfile,那应该可以解决您的问题。

在 PHP 的 readfile 文档中有 a solution 说明如何使用 fread 来做你想做的事。

【讨论】:

  • 使用带有flush() 和ob_flush() 的分块读取文件有点像文档中所述的那样工作。现在我遇到了下载在文件末尾附近被中断的问题。有什么想法吗?
  • @brenns10 - 该示例不使用flush() 和ob_flush()。使用这些会导致数据在显示之前进入内存,如果您的资源有限,您可能希望避免这种情况。话虽如此,我不确定为什么它在没有看到您的代码的情况下跳过结尾。可能是文件的最后几个字节没有被读取,或者您在最后一次读取后没有刷新。
  • 我已经尝试使用带有和不带有刷新命令的脚本,当我使用它们时,下载立即开始并且不使用太多内存(在我的 WAMP 上)。当我不使用刷新命令时,在下载开始之前会有相当长的延迟(我假设是 PHP 将文件读入内存),在此期间内存会出现巨大的峰值。我将发布我在原始问题中使用的代码。
  • 请更新/更正此答案。至少目前readfile 在内部已经“分块”。出现的唯一问题是输出缓冲区打开时;但这样会 any 输出 - fopen/fread/readfile 或其他。
  • “解决方案”链接失效
【解决方案2】:

你也可以用 Gordian Knot 的方式来处理这个问题——也就是说,完全回避这个问题。将文件保存在不可访问的目录中,当启动下载时,您可以简单地

$tempstring = rand();
symlink('/filestore/filename.extension', '/www/downloads' . $tempstring . '-filename.extension');
echo('Your download is available here: <a href="/downloads/' . $tempstring . '-filename.extension">');

并将 cronjob 设置为 unlink() 任何超过 10 分钟的下载链接。几乎不需要处理您的数据,也不需要按摩 HTTP 标头等。

甚至有几个图书馆就是为了这个目的。

【讨论】:

    【解决方案3】:

    如果您关心性能,有 xsendfile,可在 apache、nginx 和 lighttpd 中作为模块使用。检查readfile() doc 的用户cmets。

    这些网络服务器也有一些模块,它们接受带有附加哈希值的 url,允许在短时间内下载文件。这也可以用来解决授权问题。

    【讨论】:

    • 这听起来像是一个有用的选择,但这是我可以在共享托管服务器上做的事情吗?我认为托管公司有一些他们使用的设置模块,所以我可能无法在共享服务器上使用它。
    • 好,你已经回答了你的问题:) 问他们..
    【解决方案4】:

    为了从服务器下载大文件,我在 php.ini 文件中更改了以下设置:

    Upload_max_filesize  - 1500 M
    Max_input_time  - 1000
    Memory_limit    - 640M
    Max_execution_time -  1800
    Post_max_size - 2000 M
    

    现在,我可以在服务器上上传和下载 175MB 的视频。 因为,我有专用服务器。因此,进行这些更改很容易。

    下面是下载文件的 PHP 脚本。对于大文件,我没有对此代码 sn-p 进行任何更改。

    // Begin writing headers
    
                ob_clean(); // Clear any previously written headers in the output buffer
    
                if($filetype=='application/zip')
                {
                    if(ini_get('zlib.output_compression'))
                        ini_set('zlib.output_compression', 'Off');
                    $fp = @fopen($filepath, 'rb'); 
                    if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE"))
                    {
                        header('Content-Type: "$content_type"');
                        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: ".filesize(trim($filepath)));
                    }
                    else
                    {
                        header('Content-Type: "$content_type"');
                        header('Content-Disposition: attachment; filename="'.$filename.'"');
                        header("Content-Transfer-Encoding: binary");
                        header('Expires: 0');
                        header('Pragma: no-cache');
                        header("Content-Length: ".filesize(trim($filepath)));
                    }
    
                    fpassthru($fp);
                    fclose($fp);
                }
                elseif($filetype=='audio'|| $filetype=='video')
                { 
                    global $mosConfig_absolute_path,$my;
                    ob_clean();
                    header("Pragma: public");
                    header('Expires: 0');
                    header('Cache-Control: no-store, no-cache, must-revalidate');
                    header('Cache-Control: pre-check=0, post-check=0, max-age=0');
                    header("Cache-Control: public");    
                    header("Content-Description: File Transfer");
                    header("Content-Type: application/force-download"); 
                    header("Content-Type: $content_type");
                    header("Content-Length: ".filesize(trim($filepath)));
                    header("Content-Disposition: attachment; filename=\"$filename\"");
                    // Force the download           
                    header("Content-Transfer-Encoding: binary");            
                    @readfile($filepath);
                }
                  else{ // for all other types of files except zip,audio/video
                    ob_clean();
                    header("Pragma: public");
                    header('Expires: 0');
                    header('Cache-Control: no-store, no-cache, must-revalidate');
                    header('Cache-Control: pre-check=0, post-check=0, max-age=0');
                    header("Cache-Control: public");    
                    header("Content-Description: File Transfer");
                    header("Content-Type: $content_type");
                    header("Content-Length: ".filesize(trim($filepath)));
                    header("Content-Disposition: attachment; filename=\"$filename\"");
                    // Force the download           
                    header("Content-Transfer-Encoding: binary");            
                    @readfile($filepath);       
                }
                exit;
    

    【讨论】:

      猜你喜欢
      • 2015-03-24
      • 2020-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-16
      • 1970-01-01
      • 2016-10-21
      • 1970-01-01
      相关资源
      最近更新 更多