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

使用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-stream
  • Content-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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 20:44:08