代码之家  ›  专栏  ›  技术社区  ›  Jeffrey Blake

PHP文件服务脚本:下载不可靠?

  •  2
  • Jeffrey Blake  · 技术社区  · 14 年前

    这篇文章的开头是关于serverfault的一个问题( https://serverfault.com/questions/131156/user-receiving-partial-downloads )但我确定我们的PHP脚本是罪魁祸首。所以我在这里发布了一个更新的问题,关于我认为实际问题是什么。

    我使用一个PHP脚本来验证权限,然后为我的网站的用户提供一个文件来下载。大多数情况下,这是可行的,但最近有一个用户发现了较大下载量的问题。对于大小大于100MB的文件,他只能获得大约80%的下载量。此外,此脚本的所有下载都无法报告文件大小。此外,测试表明,如果给定直接链接(此时报告文件大小),同一用户可以可靠地下载每个失败的文件。

    下面是我们用来提供文件的相关代码片段:

    header("Content-type:$contenttype");
    $len = filesize($filename);
    header("Content-Length: $len");
    header("Content-Disposition: attachment; filename=".$title.".".$ext);
    readfile($filename);
    

    请注意,$contenttype、$filename、$title和$ext在到达这里之前都设置正确。这些都经过了三次检查。这些都不是问题所在。另外,$len也提供了正确的文件大小。

    在研究这个问题的时候,我遇到了这个帖子: Content-Length header always zero

    看来我也遇到了同样的问题。当我使用脚本时,文件上会有分块编码,并且没有为内容长度设置大小。我假设大量下载会出错,导致他在文件结束前得到一个零长度的块。

    下面是直接请求的标题:

    http://www.grinderschool.com/videos/zfff5061b65ae00e8b21/KillsAids021.wmv
    
    GET /videos/zfff5061b65ae00e8b21/KillsAids021.wmv HTTP/1.1
    Host: www.grinderschool.com
    User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 (.NET CLR 3.5.30729)
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Accept-Language: en-us,en;q=0.5
    Accept-Encoding: gzip,deflate
    Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
    Keep-Alive: 115
    Connection: keep-alive
    Referer: http://www.grinderschool.com/phpBB3/viewtopic.php?f=14&p=29468
    Cookie: style_cookie=printonly; phpbb3_7c544_u=2; phpbb3_7c544_k=44b832912e5f887d; phpbb3_7c544_sid=e8852df42e08cc1b2250300c2897f78f; __utma=174624884.2719561324781918700.1251850714.1270986325.1270989003.575; __utmz=174624884.1264524375.411.12.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=low%20stakes%20poker%20videos; phpbb3_cmviy_k=; phpbb3_cmviy_u=2; phpbb3_cmviy_sid=d8df5c0943863004ca40ef9c392d371d; __utmb=174624884.4.10.1270989003; __utmc=174624884
    Pragma: no-cache
    Cache-Control: no-cache
    
    HTTP/1.1 200 OK
    Date: Sun, 11 Apr 2010 12:57:41 GMT
    Server: Apache/2.2.14 (Unix) mod_ssl/2.2.14 OpenSSL/0.9.8l DAV/2 mod_auth_passthrough/2.1 FrontPage/5.0.2.2635
    Last-Modified: Sun, 04 Apr 2010 12:51:06 GMT
    Etag: "eb42d6-7d9b843-48368aa6dc280"
    Accept-Ranges: bytes
    Content-Length: 131708995
    Keep-Alive: timeout=10, max=30
    Connection: Keep-Alive
    Content-Type: video/x-ms-wmv
    

    下面是我的脚本对请求的响应:

    http://www.grinderschool.com/download_video_test.php?t=KillsAids021&format=wmv
    
    GET /download_video_test.php?t=KillsAids021&format=wmv HTTP/1.1
    Host: www.grinderschool.com
    User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 (.NET CLR 3.5.30729)
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
    Accept-Language: en-us,en;q=0.5
    Accept-Encoding: gzip,deflate
    Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
    Keep-Alive: 115
    Connection: keep-alive
    Cookie: style_cookie=printonly; phpbb3_7c544_u=2; phpbb3_7c544_k=44b832912e5f887d; phpbb3_7c544_sid=e8852df42e08cc1b2250300c2897f78f; __utma=174624884.2719561324781918700.1251850714.1270986325.1270989003.575; __utmz=174624884.1264524375.411.12.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=low%20stakes%20poker%20videos; phpbb3_cmviy_k=; phpbb3_cmviy_u=2; phpbb3_cmviy_sid=d8df5c0943863004ca40ef9c392d371d; __utmb=174624884.4.10.1270989003; __utmc=174624884
    
    HTTP/1.1 200 OK
    Date: Sun, 11 Apr 2010 12:58:02 GMT
    Server: Apache/2.2.14 (Unix) mod_ssl/2.2.14 OpenSSL/0.9.8l DAV/2 mod_auth_passthrough/2.1 FrontPage/5.0.2.2635
    X-Powered-By: PHP/5.2.11
    Content-Disposition: attachment; filename=KillsAids021.wmv
    Vary: Accept-Encoding
    Content-Encoding: gzip
    Keep-Alive: timeout=10, max=30
    Connection: Keep-Alive
    Transfer-Encoding: chunked
    Content-Type: video/x-ms-wmv
    

    所以问题是……我该怎么做才能让脚本中的下载正常工作?同样,对于99%的用户来说,它是按原样工作的(尽管我发现现在没有报告文件大小,因此无法计算下载的时间估计,这很烦人)。

    3 回复  |  直到 14 年前
        1
  •  2
  •   mattbasta    14 年前

    这是您的gzip压缩。当您指定内容长度但打开压缩时,它会将所有内容都粘起来。在我身上发生过几次:试着在脚本中关闭它。

    一般来说,您可以通过以下方式打开它:

    ob_start("ob_gzhandler");
    

    …所以请把这句话说出来。如果代码中没有这样的设置,那么很可能在php.ini文件的某个地方或apache.conf/conf.d中有一个设置。

    希望这有帮助!

        2
  •  2
  •   bobince    14 年前
    Content-Encoding: gzip
    

    嗯,大概是PHP的吧 zlib.output_compression 是这样做的。(看起来不像阿帕奇的模型。)

    试着把它关掉,看看是不是它在强制分块编码。您不想压缩像wmv这样已经高度压缩的文件类型的下载。

    但是,分块编码只能解释缺少大小报告。下载应该仍然有效。是否有可能被超时(如php的 set_time_limit 或阿帕奇 Timeout )?

        3
  •  0
  •   zaf    14 年前

    如果是脚本,那么您是否尝试使用readfile()函数的替代函数来读取和输出一次一点?这背后的原因可能是某个地方达到了内存限制,但它失败了。

    http://php.net/manual/en/function.readfile.php :

    function readfile_chunked ($filename) {
      $chunksize = 1*(1024*1024); // how many bytes per chunk
      $buffer = '';
      $handle = fopen($filename, 'rb');
      if ($handle === false) {
        return false;
      }
      while (!feof($handle)) {
        $buffer = fread($handle, $chunksize);
        print $buffer;
      }
      return fclose($handle);
    } 
    

    另外,尽可能频繁地刷新输出。