如何使用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
相关产品推荐
相关产品推荐

