【问题标题】:Apache/PHP experts? Large file download fails: empty file is saved with zero KBApache/PHP 专家?大文件下载失败:空文件保存为零 KB
【发布时间】:2009-11-25 08:07:26
【问题描述】:

我有一个网页,我用它来让经过身份验证的用户访问下载文件。这些文件位于根文件夹中(通常不能通过 HTTP 访问),我使用 PHP 脚本和 fopen 函数来生成开始下载文件的网页。它适用于其中三个文件(所有文件均小于 200KB),但第四个文件(即 40MB)保存为空文件。

如果我将 40MB 文件移动到 HTTP 可访问的文件夹中,文件的链接可以正常工作,但它显然不能提供我想要的受限访问。

我已在脚本中确认该文件正在被 PHP 找到,但由于某种原因,当发送标头时,40MB 文件立即完成下载,为 0KB。

有趣的是,这段代码在我的本地开发服务器上运行良好,但在生产服务器上却不行。两者都运行相同版本的 PHP (5.2.10),并且具有相同的内存和执行时间设置(32M 和 30 秒)。我尝试提高生产 memory_limit 和 max_execution_time 值(到 64M 和 60secs),但没有效果。

有什么想法吗?这是我的 PHP 脚本(更改文件名以保护无知):

<?php
// Download filename given in the $_GET['filename'] variable
$filepath = $_SERVER['DOCUMENT_ROOT'] . '/../';
if (isset($_REQUEST['type'])) {
 switch (strtoupper($_REQUEST['type'])) {
 case 'EVAL':
  $filename = "littlefile1.zip";
  break;
 case 'EXE':
  $filename = "bigfile.zip";
  break;
 case 'RPT':
  $filename = "littlefile2.zip";
  break;
 case 'UPD':
  $filename = "littlefile3.zip";
  break;
 default:
  die();
 }


 header("Content-Disposition: attachment; filename=" . $filename);
 header("Content-Length: " . filesize($filepath . $filename));
 header("Content-Type: application/octet-stream");
 $fp=fopen($filepath . $filename, 'rb');
 fpassthru($fp);
 ob_flush();
 fclose($fp);
}
?>

提前致谢!

响应 Greg 的评论,我得到的标题错误如下所示:

Date: Sun, 29 Nov 2009 19:58:16 GMT
Server: Apache/2.2.3 (Red Hat)
X-Powered-By: PHP/5.2.10
Content-Disposition: attachment; filename=bigfile.zip
Content-Encoding: gzip
Vary: Accept-Encoding
Content-Length: 44964864
Connection: close
Content-Type: application/octet-stream

500 Internal Server Error

【问题讨论】:

标签: php apache download


【解决方案1】:

如果您设置error_reporting(E_ALL);,您会收到任何错误吗?

我看到的唯一的东西是脚本时间限制和一些内存问题,但既然你说你已经检查了这两个问题。

【讨论】:

    【解决方案2】:

    您是否使用输出缓冲(使用 ob_start())?如果没有,你必须使用flush()而不是ob_flush(),并且你必须在发送文件之前调用它。为了安全起见,您也可以调用 ob_clean() 来清理输出缓冲区。顺便说一句,readfile() 使您不必使用 fopen()、fpassthru() 和 fclose()。

    <?php
    // Download filename given in the $_GET['filename'] variable
    $filepath = $_SERVER['DOCUMENT_ROOT'] . '/../';
    filenames['EVAL'] = "littlefile1.zip";
    filenames['EXE'] = "bigfile.zip";
    filenames['RPT'] = "littlefile2.zip";
    filenames['UPD'] = "littlefile3.zip";
    if (isset($_REQUEST['type'])) {
     $filename = filenames[strtoupper($_REQUEST['type'])];
     if (!empty($filename) && file_exists( $filepath . $filename)) {
       header("Content-Disposition: attachment; filename=" . $filename);
       header("Content-Length: " . filesize($filepath . $filename));
       header("Content-Type: application/octet-stream");
       flush();
       ob_clean();
       readfile($filepath . $filename);
     }
     exit;
    }
    

    【讨论】:

    • 感谢更简洁的代码!我已经对此进行了测试,它适用于其他文件,但我得到了与以前相同的错误。我将与我的虚拟主机核实,看看他们是否正在采取措施阻止此下载。
    【解决方案3】:

    我敢打赌,供应商将 error_reporting 设置为 0,或者将错误重定向到日志文件,因此会发生一些错误但不会被报告。

    • 你能做一个 $_REQUEST["type"] 的测试输出吗?会不会是你的 switch 语句为空?

    • 你能不能故意插入一些会导致致命错误的东西,看看是否是空白?

    • 正如 RageZ 建议的那样,您可以将 error_reporting 设置为 E_ALL 吗?

    • 默认情况下,错误可能不会显示在输出中,而是重定向到内部错误日志文件。您能否与您的供应商核实是否是这种情况?也许您有一个登录区域来查看错误日志,或者它们存储在您的目录结构中的某个位置?

    【讨论】:

      【解决方案4】:

      Content-Disposition:附件;文件名=Jobchart.msi

      那个文件真的存在吗?

      【讨论】:

      • 是的。在那里。 (我在问题中使用了不同的名称,但这是实际的文件名。)我更改了原始 cmets 以便文件名匹配。
      猜你喜欢
      • 2021-02-08
      • 2022-11-19
      • 1970-01-01
      • 2023-04-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-31
      • 1970-01-01
      相关资源
      最近更新 更多