使用Microsoft Graph API上传邮件大附件时文件损坏问题排查
问题分析与解决方案
从你的代码和描述来看,文件上传后大小显示正常但始终损坏,大概率是文件内容编码处理错误或者分块读取逻辑存在疏漏,下面是几个最可能的操作失误点:
1. contentBytes未解码为原始二进制字节
Microsoft Graph的大文件上传要求上传的是原始二进制文件内容,但如果$someFileAttachment->getContentBytes()返回的是Base64编码的字符串(很多文件处理类会默认返回Base64格式),直接写入流并分块上传的话,相当于把Base64文本当成了原始文件内容,自然会导致文件损坏。
解决方法:
先将Base64字符串解码为原始字节:
// 替换原代码中的写入流操作 fwrite($stream, base64_decode($attachment['contentBytes']));
2. 数组索引错误导致使用了错误的Attachment配置
你初始化$attachment时用了$attachment[] = [...](数组追加语法),但后续直接用$attachment['attachment']访问,这会导致索引错误。虽然你说上传后文件大小显示正确,但如果contentBytes的实际长度和AttachmentItem中声明的size不匹配,也会导致文件内容异常。
解决方法:
修正数组索引,比如直接赋值而非追加:
// 直接初始化单个attachment对象,而非数组追加 $attachment = [ "contentBytes" => $someFileAttachment->getContentBytes(), 'attachment' => [ 'AttachmentItem' => new FileAttachment( [ "attachmentType" => "file", "name" => $someFileAttachment->getName(), "size" => $someFileAttachment->getSize() ] ) ] ]; // 后续访问时直接使用$attachment['attachment']即可 $uploadSessionResult = $createUploadSession($attachment['attachment'], $draftMessageId, $emailAddress); $fileSize = $attachment['attachment']['AttachmentItem']->getSize();
3. 临时流操作冗余且易出错
你把整个文件内容写入php://temp再分块读取的逻辑有点冗余,而且如果文件很大,会额外占用内存。更高效且不易出错的方式是直接对原始字节字符串进行截取,不需要临时流。
优化后的分块读取逻辑:
// 先解码原始内容(关键步骤) $rawContent = base64_decode($attachment['contentBytes']); $i = 0; while ($i < $numFragments) { $start = $i * $fragSize; $chunkSize = min($fragSize, $fileSize - $start); $end = $start + $chunkSize - 1; // 直接截取原始字节字符串的分块内容 $data = substr($rawContent, $start, $chunkSize); $contentRange = "bytes {$start}-{$end}/{$fileSize}"; $uploadBytes($uploadSessionResult->getUploadUrl(), $chunkSize, $contentRange, $data); $i++; }
4. uploadBytes函数的请求头可能缺失或错误
确保uploadBytes函数中设置了正确的请求头:
Content-Type必须设为application/octet-streamContent-Range严格遵循bytes start-end/total的格式(注意空格和标点)Content-Length必须等于当前分块的字节数(即$chunkSize)
如果这些请求头设置错误,服务器接收的内容会异常,最终导致文件损坏。
完整修改后的代码示例
// 修正attachment初始化方式 $attachment = [ "contentBytes" => $someFileAttachment->getContentBytes(), 'attachment' => [ 'AttachmentItem' => new FileAttachment( [ "attachmentType" => "file", "name" => $someFileAttachment->getName(), "size" => $someFileAttachment->getSize() ] ) ] ]; // 创建上传会话 $uploadSessionResult = $createUploadSession($attachment['attachment'], $draftMessageId, $emailAddress); $fragSize = 320 * 1024; // 符合Graph API要求的分块大小(最小320KB,最大4MB) $fileSize = $attachment['attachment']['AttachmentItem']->getSize(); $numFragments = ceil($fileSize / $fragSize); // 解码原始二进制内容 $rawContent = base64_decode($attachment['contentBytes']); $i = 0; while ($i < $numFragments) { $start = $i * $fragSize; $chunkSize = min($fragSize, $fileSize - $start); $end = $start + $chunkSize - 1; // 直接截取分块内容 $data = substr($rawContent, $start, $chunkSize); $contentRange = "bytes {$start}-{$end}/{$fileSize}"; // 确保uploadBytes正确设置请求头:Content-Type: application/octet-stream、Content-Range、Content-Length $uploadBytes($uploadSessionResult->getUploadUrl(), $chunkSize, $contentRange, $data); $i++; }
内容的提问来源于stack exchange,提问作者BigDataFiles
相关产品推荐
相关产品推荐

