使用Microsoft Graph获取PDF附件邮件失败:Attachments为空但HasAttachments为真
问题分析与解决办法
你的代码存在几个关键问题,导致无法正常获取并下载PDF附件,具体问题和修正方案如下:
核心问题点
- 附件未实际加载:第一次获取邮件列表时没有通过
Expand("Attachments")加载附件数据;后续发起的带Expand的请求既没加await等待完成,也没将返回的带附件邮件对象赋值给循环中的message,导致原message.Attachments始终为null。 - 附件筛选逻辑错误:用
ContentId == "application/pdf"完全错误,ContentId是附件的唯一标识ID,并非文件类型。应通过ContentType(值为application/pdf)或文件名后缀(如.pdf)来筛选PDF附件。 - 文件名变量作用域错误:变量
b定义在if块内部,每次循环都会重置为0,导致所有附件都会被命名为0file.pdf,后续文件会覆盖之前的。 - 异步调用处理不当:使用
.GetAsync().Result可能引发线程死锁,建议全程用await处理异步操作。
修正后的代码
方案一:首次请求直接加载附件(适合邮件数量不多的场景)
int fileCounter = 0; var msgs = await _graphServiceClient.Users["abc@Turtles.com"].Messages.Request() .Filter("(from/emailAddress/address) eq 'noreply@adr.org'") .Top(5000) .Header("Prefer", "outlook.body-content-type='text'") .Expand("Attachments") // 直接加载附件,避免二次请求 .GetAsync(); foreach (var message in msgs) { if (message.HasAttachments && message.Attachments != null) { // 筛选所有PDF附件 var pdfAttachments = message.Attachments.OfType<FileAttachment>() .Where(a => a.ContentType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase)); foreach (var pdfAttachment in pdfAttachments) { // 用计数器+原文件名避免重复覆盖 string filePath = $@"C:\TMP\{fileCounter}_{pdfAttachment.Name}"; using (var stream = new FileStream(filePath, FileMode.CreateNew)) using (var writer = new BinaryWriter(stream)) { writer.Write(pdfAttachment.ContentBytes, 0, pdfAttachment.ContentBytes.Length); } fileCounter++; } } }
方案二:循环内单独请求带附件的邮件(适合邮件数量多,避免一次性加载大量数据)
int fileCounter = 0; var msgs = await _graphServiceClient.Users["abc@Turtles.com"].Messages.Request() .Filter("(from/emailAddress/address) eq 'noreply@adr.org'") .Top(5000) .Header("Prefer", "outlook.body-content-type='text'") .GetAsync(); foreach (var message in msgs) { if (message.HasAttachments) { // 等待异步请求完成,获取带附件的邮件对象 var messageWithAttachments = await _graphServiceClient.Users["abc@Turtles.com"] .Messages[message.Id] .Request() .Expand("Attachments") .GetAsync(); var pdfAttachments = messageWithAttachments.Attachments.OfType<FileAttachment>() .Where(a => a.ContentType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase)); foreach (var pdfAttachment in pdfAttachments) { string filePath = $@"C:\TMP\{fileCounter}_{pdfAttachment.Name}"; using (var stream = new FileStream(filePath, FileMode.CreateNew)) using (var writer = new BinaryWriter(stream)) { writer.Write(pdfAttachment.ContentBytes, 0, pdfAttachment.ContentBytes.Length); } fileCounter++; } } }
额外说明
- 确保你的Graph API权限包含
Mail.Read或更高权限,否则可能无法读取附件内容 - 使用
using语句自动释放文件流,避免资源泄漏 - 若附件体积较大,可考虑通过附件的下载URL直接获取,但常规大小的PDF用
ContentBytes即可满足需求
内容的提问来源于stack exchange,提问作者bumble_bee_tuna
相关产品推荐
相关产品推荐

