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

如何使用Symfony Http Client从字符串上传文件而不存磁盘?

不落地本地文件上传multipart API返回422错误的解决方案

问题核心

从S3通过Flysystem读取文件后,直接上传到要求multipart表单的API时持续返回422错误,但先将文件写入本地再上传就能正常运行。本质问题是上传请求缺少API要求的文件元信息(文件名、MIME类型),导致服务端无法识别上传内容格式。

可行解决方案

方案1:用Guzzle的multipart参数手动指定元信息

Guzzle的body参数传数组默认是application/x-www-form-urlencoded格式,而非multipart表单。必须用multipart参数明确构建多部分请求,同时给文件流附加文件名和MIME类型:

$fileContent = $this->documentStorage->read('remote filename.pdf');

// 创建可读写临时流并写入文件内容
$stream = fopen('php://temp', 'rb+');
fwrite($stream, $fileContent);
rewind($stream);

$params = [
    'multipart' => [
        [
            'name'     => 'bestand', // 对应API要求的表单字段名
            'contents' => $stream,
            'filename' => 'remote filename.pdf', // 必须指定,多数API会校验文件名
            'headers'  => [
                'Content-Type' => 'application/pdf' // 匹配文件的MIME类型
            ]
        ]
    ]
];

$response = $this->createClient()->request('POST', $url, $params);

方案2:正确使用FormDataPart构建表单

之前手动构建表单时,DataPart缺少必要元信息,且请求头处理错误。修正后的代码如下:

use GuzzleHttp\Psr7\DataPart;
use GuzzleHttp\Psr7\FormDataPart;

$fileContent = $this->documentStorage->read('remote filename.pdf');

// 初始化DataPart时传入完整的文件元信息
$dp = new DataPart([
    'name'     => 'bestand',
    'contents' => $fileContent,
    'filename' => 'remote filename.pdf',
    'headers'  => ['Content-Type' => 'application/pdf']
]);

$formData = new FormDataPart(['bestand' => $dp]);

// 直接使用FormDataPart提供的Body和Headers,不要手动处理字符串
$params = [
    'body'    => $formData->getBody(),
    'headers' => $formData->getHeaders()
];

$response = $this->createClient()->request('POST', $url, $params);

错误原因分析

  • 直接传php://temp流到body数组时,Guzzle无法自动获取文件名和MIME类型,导致multipart表单缺少关键元信息,服务端验证失败返回422。
  • 手动构建FormDataPart时,仅传入文件内容未指定元信息,且错误地将PreparedHeaders转为字符串后修剪,破坏了请求头的正确格式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 11:42:51