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

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:/\&lt;script\&gt;[^&lt;]*\&lt;\/script\&gt;/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);
    }
}

关键修改点说明

  1. 启用$test模式:在UploadedFile构造函数中传入第5个参数true,跳过is_uploaded_file()的检查,让验证逻辑认可手动生成的文件。
  2. 明确上传错误码:显式传递UPLOAD_ERR_OK,标记文件为“上传成功”状态。
  3. 优化临时文件处理:使用tempnam替代tmpfile(),更便于后续文件操作;请求结束后通过unlink直接删除临时文件,避免资源残留。
  4. 提取原始文件名:解析Base64前缀中的文件名(若前端传递),确保文件名符合业务预期,提升兼容性。

内容的提问来源于stack exchange,提问作者Nazmul Huda Tanvir

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 09:07:08