You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 17:05:52