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

Webhook文件上传未知错误排查及替代方案咨询

问题排查与修复方案

问题根源

Webhook接收的数据完全正常(日志显示xfile包含有效的临时文件路径,emails里也有完整的file_path),所有问题都出在你的PHP代码逻辑上:

  1. 文件移动失败:你使用了move_uploaded_file()函数,但该函数仅适用于通过HTTP表单上传的文件(即$_FILES数组中的文件)。Webhook传递的是服务器已存在的临时文件路径,不属于标准上传场景,因此该函数直接失败且无法返回有效错误信息。
  2. 数据库字段为空:文件移动失败后,$filePath变量未被赋值,直接执行SQL插入时,file_path字段传入null,触发数据库非空约束错误。
  3. 冗余逻辑:在foreach ($webhookData['emails'])循环内重复处理同一个xfile,既浪费资源又可能引发意外问题。

修复步骤

  1. 替换move_uploaded_file()为copy(),用于处理服务器本地文件的迁移。
  2. 提前检查并创建目标目录(如果不存在),确保目录有写入权限。
  3. 增加错误分支的file_path降级方案:如果文件移动失败,直接使用Webhook传递的原始file_path,避免插入空值。
  4. 将文件处理逻辑移到循环外,避免重复执行。

修复后的代码

<?php
// 调试错误配置
error_reporting(E_ALL);
ini_set('display_errors', 'Off');
ini_set('log_errors', 'On');
ini_set('error_log', __DIR__ . '/webhook_log.txt');

// 数据库连接
$servername = "servername is here";
$username = "username is here";
$password = "password is here";
$dbname = "mailgateway";
$conn = new mysqli($servername, $username, $password, $dbname);

// 检查连接
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}

// 记录原始Webhook数据
$rawWebhookData = file_get_contents('php://input');
error_log("Webhook Data Received: " . $rawWebhookData);

// 解析JSON数据
$webhookData = json_decode($rawWebhookData, true);
if ($webhookData === null) {
    $jsonLastError = json_last_error();
    $jsonLastErrorMessage = json_last_error_msg();
    $logMessage = "Error decoding webhook data. Error code: $jsonLastError, Message: $jsonLastErrorMessage";
    error_log($logMessage);

    header('Content-Type: application/json');
    echo json_encode(['success' => false, 'message' => 'An error occurred while processing the webhook data.']);
    exit;
}

// 初始化响应
$response = ['success' => true, 'message' => 'Email(s) processed successfully', 'errors' => []];

// 准备SQL语句
$insertMailStmt = $conn->prepare("INSERT INTO mails (email, mail_id, file_path, date, time) VALUES (?, ?, ?, ?, ?)");
$insertMailStmt->bind_param("sssss", $email, $mailId, $filePath, $date, $time);

// 记录解析后的数据
$logFile = __DIR__ . '/webhook_log.txt';
$logMessage = PHP_EOL . date('Y-m-d H:i:s') . ' - Webhook Data Received: ' . PHP_EOL . print_r($webhookData, true) . PHP_EOL;
file_put_contents($logFile, $logMessage, FILE_APPEND);

try {
    $conn->begin_transaction();

    // --- 处理文件(仅执行一次) ---
    $xfile = $webhookData['xfile'] ?? null;
    $uploadedFilePath = null;
    if ($xfile && $xfile['error'] === 0) {
        $targetDir = $_SERVER['DOCUMENT_ROOT'] . '/data/uploads/files/';
        // 确保目标目录存在
        if (!is_dir($targetDir)) {
            mkdir($targetDir, 0755, true);
        }

        $mailId = $webhookData['emails'][0]['mail_id'];
        $filename = 'file_' . $mailId . '_' . time() . '_' . rand(1000, 9999) . '.pdf';
        $xfilePath = $targetDir . $filename;

        // 使用copy替代move_uploaded_file处理本地文件
        if (copy($xfile['tmp_name'], $xfilePath)) {
            $uploadedFilePath = $xfilePath;
        } else {
            $logMessage = "Error copying file: " . basename($xfile['name']) . ". Error: " . (error_get_last()['message'] ?? 'Unknown error');
            error_log($logMessage);
            file_put_contents($logFile, $logMessage, FILE_APPEND);
            $response['success'] = false;
            $response['errors'][] = $logMessage;
            // 降级使用原始file_path
            $uploadedFilePath = $webhookData['emails'][0]['file_path'];
        }
    }

    // --- 处理邮件数据 ---
    foreach ($webhookData['emails'] as $emailData) {
        $email = filter_var($emailData['email'], FILTER_SANITIZE_EMAIL);
        $mailId = filter_var($emailData['mail_id'], FILTER_SANITIZE_STRING);
        $date = filter_var($emailData['date'], FILTER_SANITIZE_STRING);
        $time = filter_var($emailData['time'], FILTER_SANITIZE_STRING);
        // 使用上传后的路径或原始路径
        $filePath = $uploadedFilePath ?? $emailData['file_path'];

        // 执行插入
        $insertMailStmt->execute();
        if ($insertMailStmt->affected_rows <= 0) {
            $response['success'] = false;
            $response['errors'][] = "Error inserting record into mails table: " . $insertMailStmt->error;
        }
    }

    $conn->commit();
} catch (Exception $e) {
    $conn->rollback();
    $logMessage = "Exception: " . $e->getMessage();
    error_log($logMessage);
    file_put_contents($logFile, $logMessage, FILE_APPEND);
    $response['success'] = false;
    $response['errors'][] = $logMessage;
} finally {
    $insertMailStmt->close();
    $conn->close(); // 关闭数据库连接
}

// 记录响应
error_log("Webhook Response: " . json_encode($response));
file_put_contents($logFile, PHP_EOL . 'Webhook Response: ' . PHP_EOL . print_r($response, true), FILE_APPEND);

// 返回响应
header('Content-Type: application/json');
echo json_encode($response);
?>

额外注意事项

  • 确保/data/uploads/files/目录权限设置为755,且服务器进程(如www-data)拥有写入权限。
  • 如果Webhook传递的tmp_name文件生命周期短,建议尽快完成复制操作,避免文件被系统自动清理。
  • 可增加文件类型验证逻辑,确保仅处理PDF文件,提升安全性。

内容的提问来源于stack exchange,提问作者Freeman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 11:05:58