Laravel中Base64转UploadedFile后文件验证规则报错问题
Laravel Base64转UploadedFile后验证失败问题排查与解决
问题描述
在Laravel项目中,我通过中间件将后缀为File字段的Base64编码字符串转换为UploadedFile对象,但执行验证规则时触发了文件上传失败的错误。相关代码与报错信息如下:
中间件代码
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Illuminate\Http\UploadedFile; use Illuminate\Support\Arr; use Illuminate\Http\File; class Base64ToUploadedFileMiddleware { public function handle(Request $request, Closure $next) { $inputs = $request->request->all(); foreach($inputs as $fieldName => $fieldValue) { if(str_ends_with($fieldName, 'File') && $fieldValue != null) { $fileData = base64_decode(Arr::last(explode(',', $fieldValue))); $tempFile = tmpfile(); $tempFilePath = stream_get_meta_data($tempFile)['uri']; file_put_contents($tempFilePath, $fileData); $tempFileObject = new File($tempFilePath); $file = new UploadedFile( $tempFileObject->getPathname(), $tempFileObject->getFilename(), $tempFileObject->getMimeType() ); app()->terminating(function () use ($tempFile) { fclose($tempFile); }); $request->request->remove($fieldName); $request->files->set($fieldName, $file); } } return $next($request); } }
验证规则(DocumentRequest)
public function rules() { $alignments = ['LEFT', 'CENTER', 'RIGHT']; $updateId = request('id', null); $uniqueRule = 'unique:documents,header'; if($updateId) $uniqueRule .= ',' . $updateId . ',id,created_by,' . auth()->user()->id; return [ 'headerText' => [ 'required', 'max:255', $uniqueRule ], 'headerTextAlignment' => [ 'required', 'in:' . implode(',', $alignments) ], 'headerLogoFile' => [ 'nullable', 'file', 'mimes:jpeg,jpg,png', 'size:2000'], 'headerLogoAlignment' => [ 'nullable', 'in:' . implode(',', $alignments) ], 'content' => [ 'required', 'not_regex:/\<script\>[^<]*\<\/script\>/i' ], 'pdfFile' => [ 'required', 'file', 'mimes:pdf', 'size:100000' ] ]; }
报错信息
{ "message": "The given data was invalid.", "errors": { "headerLogoFile": [ "The header logo file failed to upload." ] } }
原因分析
报错核心原因是手动创建的UploadedFile未通过Symfony的上传有效性检查:
- Symfony的
UploadedFile类默认会调用is_uploaded_file()函数验证文件是否通过HTTP POST上传,而我们手动生成的临时文件不满足这个条件,导致isValid()方法返回false。 - 原代码中创建
UploadedFile时未显式设置$test参数(默认值为false),无法跳过上述检查;同时未明确指定上传错误码为UPLOAD_ERR_OK,进一步触发验证逻辑报错。
解决方法
修改中间件中UploadedFile的创建逻辑,添加跳过上传检查的参数,并优化临时文件处理:
修改后的完整中间件代码
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Illuminate\Http\UploadedFile; use Illuminate\Support\Arr; use Illuminate\Http\File; class Base64ToUploadedFileMiddleware { public function handle(Request $request, Closure $next) { $inputs = $request->request->all(); foreach($inputs as $fieldName => $fieldValue) { if(str_ends_with($fieldName, 'File') && $fieldValue != null) { // 拆分Base64字符串,分离前缀与数据部分 [$base64Prefix, $fileData] = explode(',', $fieldValue, 2); $fileData = base64_decode($fileData); // 创建临时文件,使用tempnam确保文件路径可访问 $tempFilePath = tempnam(sys_get_temp_dir(), 'laravel_upload_'); file_put_contents($tempFilePath, $fileData); $tempFileObject = new File($tempFilePath); // 尝试从Base64前缀提取原始文件名(若前端传递) $originalName = 'upload.' . $tempFileObject->getExtension(); if(preg_match('/name=([^;]+)/', $base64Prefix, $matches)) { $originalName = $matches[1]; } // 创建UploadedFile,设置test模式跳过上传检查 $file = new UploadedFile( $tempFileObject->getPathname(), $originalName, $tempFileObject->getMimeType(), UPLOAD_ERR_OK, true ); // 请求结束后清理临时文件 app()->terminating(function () use ($tempFilePath) { if(file_exists($tempFilePath)) { unlink($tempFilePath); } }); $request->request->remove($fieldName); $request->files->set($fieldName, $file); } } return $next($request); } }
关键修改点说明
- 启用
$test模式:在UploadedFile构造函数中传入第5个参数true,跳过is_uploaded_file()的检查,让验证逻辑认可手动生成的文件。 - 明确上传错误码:显式传递
UPLOAD_ERR_OK,标记文件为“上传成功”状态。 - 优化临时文件处理:使用
tempnam替代tmpfile(),更便于后续文件操作;请求结束后通过unlink直接删除临时文件,避免资源残留。 - 提取原始文件名:解析Base64前缀中的文件名(若前端传递),确保文件名符合业务预期,提升兼容性。
内容的提问来源于stack exchange,提问作者Nazmul Huda Tanvir
相关产品推荐
相关产品推荐

