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

php.ini中post_max_size引发Symfony文件上传500错误及内存问题

解决方案:提前拦截超大POST请求(无需修改php.ini)

问题根源

当上传文件同时超过你的5MB业务限制和php.ini的post_max_size时,PHP会在Symfony内核处理请求之前就触发警告,并且无法正确解析POST请求体。此时Symfony后续的请求处理会因损坏的请求数据引发内存耗尽,最终返回500错误。你之前的RequestEvent监听器执行时机太晚,无法避免这些底层问题。

可行方案

方案1:使用Symfony HttpKernel中间件(PHP层面拦截)

中间件会在Symfony处理请求的最早期执行,能在请求体被解析前就检查大小并返回错误,避免PHP加载超大请求体。

步骤1:创建中间件类

namespace App\Middleware;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;

class MaxPostSizeMiddleware implements HttpKernelInterface
{
    public function __construct(private HttpKernelInterface $next)
    {
    }

    public function handle(Request $request, int $type = self::MAIN_REQUEST, bool $catch = true): Response
    {
        // 仅处理主请求
        if ($type !== self::MAIN_REQUEST) {
            return $this->next->handle($request, $type, $catch);
        }

        // 只针对文件上传的POST请求(可根据你的路由调整)
        if ($request->isMethod('POST') && str_starts_with($request->getPathInfo(), '/api/documents')) {
            $contentLength = $request->headers->get('Content-Length');
            if (!$contentLength) {
                return $this->next->handle($request, $type, $catch);
            }

            $contentLength = (int) $contentLength;
            // 你的业务限制:5MB
            $maxFileSize = 5 * 1024 * 1024;
            // 取业务限制和php.ini限制的较小值,避免触发PHP的post_max_size警告
            $postMaxSize = $this->getPostMaxSize();
            $maxAllowed = $postMaxSize > 0 ? min($maxFileSize, $postMaxSize) : $maxFileSize;

            if ($contentLength > $maxAllowed) {
                return new Response(
                    json_encode(['message' => 'La taille du fichier est trop grande.']),
                    Response::HTTP_UNPROCESSABLE_ENTITY,
                    ['Content-Type' => 'application/json']
                );
            }
        }

        return $this->next->handle($request, $type, $catch);
    }

    private function getPostMaxSize(): int
    {
        $maxSize = ini_get('post_max_size');
        if (!preg_match('/^(\d+)([KMG])$/i', $maxSize, $matches)) {
            return 0;
        }

        $value = (int) $matches[1];
        switch (strtoupper($matches[2])) {
            case 'G':
                $value *= 1024;
            case 'M':
                $value *= 1024;
            case 'K':
                $value *= 1024;
        }

        return $value;
    }
}

步骤2:注册中间件

在config/services.yaml中添加以下配置,确保中间件以最高优先级执行:

services:
    App\Middleware\MaxPostSizeMiddleware:
        tags:
            - { name: kernel.middleware, priority: 256 }

步骤3:抑制PHP警告(可选)

即使中间件提前拦截,PHP仍可能触发POST Content-Length exceeds limit的警告。若不想让警告输出到终端,可在public/index.php最顶部添加:

<?php
// 临时抑制E_WARNING,仅针对请求初始化阶段
$originalErrorLevel = error_reporting();
error_reporting($originalErrorLevel & ~E_WARNING);

use App\Kernel;
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';

// 恢复原错误级别
error_reporting($originalErrorLevel);

return function (array $context) {
    return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
};

方案2:Web服务器层面拦截(更高效)

如果有权限修改Web服务器配置,直接在Nginx/Apache层面检查请求大小,请求根本不会到达PHP,彻底避免内存问题。

Nginx配置示例

location /api/documents {
    if ($request_method = POST) {
        # 限制5MB(5*1024*1024=5242880字节)
        if ($content_length > 5242880) {
            return 422 '{"message":"La taille du fichier est trop grande."}';
        }
    }
    # 你的原有反向代理/FastCGI配置
}

Apache配置示例

RewriteEngine On
# 拦截/api/documents的POST请求,大小超过5MB则返回422
RewriteCond %{REQUEST_METHOD} POST
RewriteCond %{REQUEST_URI} ^/api/documents
RewriteCond %{CONTENT_LENGTH} >5242880
RewriteRule ^ - [R=422,L]

效果验证

使用上述方案后,上传超过5MB的文件时,服务器会直接返回422错误,不会触发PHP的内存耗尽或500错误,终端也不会输出大量报错信息。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 03:39:51