【问题标题】:PHP Download stops on 99% with download manager使用下载管理器,PHP 下载停止 99%
【发布时间】:2014-07-14 18:39:07
【问题描述】:

我有一个带有文件的服务器,人们可以下载这些文件。我使用此代码发送下载:

<?php
function dl_file_resumable($file, $is_resume = true)
{
    //First, see if the file exists
    if (!is_file($file)) {
        header("HTTP/1.1 400 Invalid Request");
        die("<h3>File Not Found</h3>");
    }

    //Gather relevent info about file
    $size = filesize($file);
    $fileinfo = pathinfo($file);

    //workaround for IE filename bug with multiple periods / multiple dots in filename
    //that adds square brackets to filename - eg. setup.abc.exe becomes setup[1].abc.exe
    $filename = (isset($_SERVER['HTTP_USER_AGENT']) && strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE')) ?
                  preg_replace('/\./', '%2e', $fileinfo['basename'], substr_count($fileinfo['basename'], '.') - 1) :
                  $fileinfo['basename'];

    $file_extension = strtolower($fileinfo['extension']);

    //This will set the Content-Type to the appropriate setting for the file
    require 'mineTypes.php';

    // Check if the file extension is in $ctype_array & return the value. If not, send default.
    $ctype = array_key_exists($file_extension, $ctype_array) ? $ctype_array[$file_extension] : 'application/force-download';

    $is_resume = ($is_resume && isset($_SERVER['HTTP_RANGE']));

    //check if http_range is sent by browser (or download manager)
    if($is_resume)
    {
        $arr = explode('=', $_SERVER['HTTP_RANGE'], 2);
        if(isset($arr[1]))   
            list($size_unit, $range_orig) = $arr;
        else list($size_unit) = $arr;

        if ($size_unit == 'bytes')
        {
            //multiple ranges could be specified at the same time, but for simplicity only serve the first range
            //http://tools.ietf.org/id/draft-ietf-http-range-retrieval-00.txt
            $arr3 = explode(',', $range_orig, 2);
            if(isset($arr3[1]))
                list($range, $extra_ranges) = $arr3;
            else
                list($range) = $arr3;
        }
        else
        {
            $range = '';
        }
    }
    else
    {
        $range = '';
    }

    //figure out download piece from range (if set)
    $arr2 = explode('-', $range, 2);
    if(isset($arr2[1]))
        list($seek_start, $seek_end) = $arr2;
    else
        list($seek_start) = $arr2;

    //set start and end based on range (if set), else set defaults
    //also check for invalid ranges.
    $seek_end = (empty($seek_end)) ? ($size - 1) : min(abs(intval($seek_end)),($size - 1));
    $seek_start = (empty($seek_start) || $seek_end < abs(intval($seek_start))) ? 0 : max(abs(intval($seek_start)),0);

    //add headers if resumable
    if ($is_resume)
    {
        //Only send partial content header if downloading a piece of the file (IE workaround)
        if ($seek_start > 0 || $seek_end < ($size - 1))
        {
            header('HTTP/1.1 206 Partial Content');
        }

        header('Accept-Ranges: bytes');
        header('Content-Range: bytes '.$seek_start.'-'.$seek_end.'/'.$size);
    }

    header('Content-Type: ' . $ctype);
    header('Content-Length: '.($seek_end - $seek_start + 1));
    header('Content-Disposition: attachment; filename="' . $filename . '"');
    header('Accept-Ranges: bytes');

    //reset time limit for big files
    set_time_limit(0);

    //open the file
    $fp = fopen($file, 'rb');

    //seek to start of missing part
    fseek($fp, $seek_start);

    //start buffered download
    $bytes = 0;
    while(!feof($fp))
    {
        print(fread($fp, 1024*8));
        flush();

        $bytes = $bytes + 1024 * 8;

        ob_flush();        
    }

    fclose($fp);
    exit;
}

当我通过浏览器下载文件时,一切都很好,但是当我通过下载管理器(如 IDM)下载文件时 - 下载在 99% 时停止。

怎么了?

【问题讨论】:

  • 如果仅在下载管理器上失败,则这是客户端问题。尝试使用不同的下载管理器,这可能只是您正在使用的下载管理器的问题。
  • @DaveChen 它也发生在其他人身上,不仅仅是我自己
  • 我认为您需要对此进行一些调试。在命令行上尝试wget,您应该能够发送带有请求的范围标头。我希望所有完整的块都可以正常工作,但是最后一个(不完整的)项目以某种方式中断。查看该部分的响应标头中的内容。
  • 我看到Content-Length。确保您的计算正确并且您没有透明的 HTTP 压缩。 (尝试删除它是否可以解决问题。)

标签: php content-length http-content-range


【解决方案1】:

您需要先执行ob_flush,然后执行flush

ob_flush 刷新您使用 ob_start 之类的函数创建的输出缓冲区

flush 将 PHP 脚本本身的缓冲输出刷新到其调用者。

您最后的ob_flush 将更多内容放入PHP 脚本缓冲区,但没有得到flushed,因为您exit;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-01-06
    • 1970-01-01
    • 2018-06-10
    • 2012-10-31
    • 1970-01-01
    • 1970-01-01
    • 2020-11-09
    相关资源
    最近更新 更多