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
相关产品推荐
相关产品推荐

