PHP中带附件的消息无法发送至Discord的技术求助
解决Discord API发送带附件PDF的400错误问题
你的问题出在错误构建了multipart/form-data请求,Discord的带附件消息要求请求采用多部分表单格式,但你之前的写法要么把文件对象塞进JSON(无法序列化),要么错误地设置请求头并直接发送JSON字符串,导致Discord无法识别有效内容,返回"Cannot send an empty message"。
正确实现代码
使用Guzzle的multipart选项来构建请求,这是处理文件上传的标准方式,Guzzle会自动处理Content-Type和边界等细节:
$discordBaseUrl = 'https://discord.com/api'; $channelId = '1882390272832675344'; // 注意:服务器上要使用Laravel的storage_path获取正确路径,不要用本地C盘路径 $payrollPath = storage_path('app/payrolls/306_payroll.pdf'); $fileName = pathinfo($payrollPath)['filename'] . '.pdf'; $httpClient = new Client([ 'verify' => false, 'headers' => [ 'Authorization' => config('local.discord_token'), 'Accept' => 'application/json', ], ]); $response = $httpClient->post("$discordBaseUrl/channels/$channelId/messages", [ 'multipart' => [ // 第一个部分:消息内容的JSON payload [ 'name' => 'payload_json', 'contents' => json_encode([ 'content' => 'A payroll has been sent to you.', 'attachments' => [ [ 'id' => 0, // 对应下面files[0]的索引 'description' => 'Payroll description.', 'filename' => $fileName ] ] ], JSON_UNESCAPED_UNICODE) ], // 第二个部分:要上传的PDF文件 [ 'name' => 'files[0]', // 索引要和attachments里的id一致 'contents' => fopen($payrollPath, 'r'), 'filename' => $fileName, 'headers' => [ 'Content-Type' => 'application/pdf' ] ] ] ]); $responseBody = $response->getBody()->getContents();
关键修正点
- 不要手动设置
Content-Type: multipart/form-data:Guzzle在使用multipart选项时会自动生成正确的Content-Type(包含边界字符串),手动设置会破坏请求格式。 - 拆分payload和文件:
payload_json字段存消息的JSON内容,files[N]字段存文件流,两者通过attachments里的id关联(id=N对应files[N])。 - 文件路径修正:服务器上不能用本地Windows路径,改用Laravel的
storage_path()函数获取正确的存储文件路径。 - 不要用
json_encode处理整个请求体:multipart请求的每个部分都是独立的,文件部分需要传入文件流而非序列化对象。
为什么之前的代码失败
- 第一段代码:把
curl_file_create生成的对象塞进JSON,JSON无法序列化CURLFile对象,导致Discord收到的内容无效,判定为空消息。 - 第二段代码:错误设置
Content-Disposition请求头,且直接发送JSON字符串作为请求体,Discord无法解析multipart格式,依然判定消息为空。
内容的提问来源于stack exchange,提问作者Hopex Development
相关产品推荐
相关产品推荐

