PHP调用readfile下载XAMPP服务器处理后Excel文件无法打开求助
问题描述
我有一段PHP脚本,用来执行Python脚本处理用户通过HTML界面上传的Excel工作簿,脚本运行正常,但使用readfile()函数下载处理完成后的文件时,遇到无法打开的问题(提示‘文件格式或文件扩展名无效’)。该Excel文件的大小和扩展名看似都正常,服务器与我的本地电脑均已安装Office 365,脚本代码如下:
<?php ini_set('max_execution_time', 400); if(isset($_POST['upload'])) { $targetDir = "C:\\SSProcess\\ExcelFiles\\"; // Modified target directory path // Ensure the directory exists if (!file_exists($targetDir)) { mkdir($targetDir, 0777, true); } $excel_path = $targetDir . basename($_FILES["excelFile"]["name"]); $fileType = strtolower(pathinfo($excel_path, PATHINFO_EXTENSION)); if ($fileType != "xls" && $fileType != "xlsx") { echo "Only Excel files are allowed."; exit(); } if (move_uploaded_file($_FILES["excelFile"]["tmp_name"], $excel_path)) { echo "The file ". basename( $_FILES["excelFile"]["name"]). " has been uploaded."; echo "file path = ", $excel_path, " ."; // Execute VBScript using cscript from cmd with admin privileges //$vbscript = "C:\\SSProcess\\Scripts\\200-Full_Sheet_Extract.vbs"; //$cmd_command = "runas /user:FPG\\bot_runner2 \"cmd /c cscript \\\"" . $vbscript . "\\\"\""; //exec($cmd_command, $vbscript_output, $vbscript_return_code); // Execute DuplicatedPerType.py with the uploaded file as an argument $pythonScript1 = "C:/Users/bot_runner2/AppData/Local/Programs/Python/Python310/python.exe C:/Users/bot_runner2/Desktop/SS/Script/DuplicatedPerType.py " . escapeshellarg($excel_path); exec($pythonScript1, $output1, $returnCode1); // Check if DuplicatedPerType.py executed successfully if ($returnCode1 === 0) { echo "DuplicatedPerType.py executed successfully."; // Execute Type&SubType.py (enclose path in double quotes) $pythonScript2 = "C:/Users/bot_runner2/AppData/Local/Programs/Python/Python310/python.exe \"C:/Users/bot_runner2/Desktop/SS/Script/Type&SubType.py\" " . escapeshellarg($excel_path); exec($pythonScript2, $output2, $returnCode2); // Check if Type&SubType.py executed successfully if ($returnCode2 === 0) { echo "Type&SubType.py executed successfully."; // Copy the Excel file to the ProcessedFiles directory $newDir = "C:\\SSProcess\\ProcessedFiles\\"; // New directory path $newExcelPath = $newDir . basename($_FILES["excelFile"]["name"]); // Ensure the new directory exists if (!file_exists($newDir)) { mkdir($newDir, 0777, true); } // Copy the file if (copy($excel_path, $newExcelPath)) { echo "Excel file copied successfully."; sleep(3); // Set the appropriate Content-Type header for .xlsx files header("Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); // Set Content-Disposition header to force download with the correct filename header("Content-Disposition: attachment; filename=\"" . basename($newExcelPath) . "\""); // Output the file contents readfile($newExcelPath); exit(); } else { echo "Failed to copy Excel file."; } // Redirect back to SystemSummary.html //header('Location: http://10.1.4.63:8080/HTML/SystemSummary.html'); //exit(); } else { echo "Error executing Type&SubType.py."; // You can handle errors here } } else { echo "Error executing DuplicatedPerType.py."; // You can handle errors here } } else { echo "Sorry, there was an error uploading your file."; } } ?>
问题排查与解决方法
1. 输出内容污染(核心问题)
你在readfile()之前输出了大量文本(比如上传成功提示、脚本执行成功提示),这些内容会被附加到Excel文件的开头,直接破坏文件结构,导致Office无法识别。
解决:
- 移除所有
header()和readfile()之前的echo语句,改用日志记录调试信息:
// 替换echo为日志写入 file_put_contents('process_log.txt', "文件已上传:". basename($_FILES["excelFile"]["name"]). "\n", FILE_APPEND);
- 如果需要给用户反馈,可将信息存入
$_SESSION,下载完成后跳转回页面再显示。
2. MIME类型未区分文件格式
当前代码只设置了xlsx的Content-Type,若用户上传xls格式文件,会导致MIME类型不匹配,触发格式错误提示。
解决:
根据文件后缀动态设置Content-Type:
$fileType = strtolower(pathinfo($newExcelPath, PATHINFO_EXTENSION)); if ($fileType === 'xlsx') { header("Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); } elseif ($fileType === 'xls') { header("Content-Type: application/vnd.ms-excel"); }
3. 文件写入未完成就开始下载
sleep(3)是不可靠的等待方式,Python脚本可能还没完全写完文件就被复制和下载,导致文件损坏。
解决:
- 确保Python脚本在写入文件后完成资源释放(比如
pandas写入后调用close(),或使用with语句)。 - 替换
sleep()为文件大小稳定检查:
$prevSize = -1; $waitCount = 0; // 最多等待10秒 while ($waitCount < 10) { clearstatcache(true, $excel_path); $currentSize = filesize($excel_path); if ($currentSize === $prevSize) { break; } $prevSize = $currentSize; sleep(1); $waitCount++; }
4. 输出缓冲区干扰
PHP的输出缓冲区可能残留内容,破坏文件输出。
解决:
在发送头信息前清空缓冲区:
ob_clean(); flush(); // 再设置header和readfile
5. 权限与文件完整性检查
- 确认
$newExcelPath对应的文件有可读权限,PHP进程能访问该文件。 - 对比服务器上的原始处理文件和下载后的文件MD5值,确认是否在传输过程中损坏。
内容的提问来源于stack exchange,提问作者Abed Al Rahman Hussien Balhawa
相关产品推荐
相关产品推荐

