C#开发邮件发送PDF附件时,接收端收到的文件体积异常偏小无法打开
C# 邮件发送PDF附件缩水、无法打开问题排查
代码中可直接定位的问题点
- 冗余的无效base64转码流程:现有逻辑将下载到的PDF字节先转成base64字符串,再转回字节流塞到附件里,这步没有任何业务价值,除了平白占用双倍内存,还可能因为长字符串截断、转码编码不匹配导致原始字节损坏
- 未做下载内容有效性校验:代码里没有校验
client.DownloadData(pdfUrl)返回的字节是不是真的目标PDF,如果URL需要鉴权、存在跳转,WebClient默认会跟随跳转但不会带上原始请求的身份凭证,最终下载到的往往是401/403对应的HTML错误页(大小普遍在3-6KB,刚好匹配遇到的5KB附件特征) - 附件配置缺失:没有显式指定附件的MIME类型,靠系统根据文件后缀自动识别时,如果服务器MIME映射不全,会把PDF识别成纯文本类型,传输过程中会被邮件网关做文本转码处理,破坏二进制结构
- 流生命周期无管控:创建的MemoryStream没有做作用域限定,实际运行中如果被GC提前回收、或是在Send方法执行过程中被关闭,会导致附件内容只被读取了一小部分就中断,最终附件体积远小于原始大小
- 传输编码不匹配:默认的附件传输编码可能被设置为QuotedPrintable,二进制PDF流用这个编码传输时会对高位字节做转义,遇到网关规则限制就会被截断,无法完整传输
修正后的参考实现
MailMessage oMail = new MailMessage(); byte[] pdfBytes; using (WebClient client = new WebClient()) { pdfBytes = client.DownloadData(pdfUrl); // 增加下载内容校验,第一时间确认是否拿到正确的PDF流 Console.WriteLine($"实际下载的PDF字节长度:{pdfBytes.Length}"); } // 用using保证流在Send完成前有效,发送完成后自动释放 using (MemoryStream strm = new MemoryStream(pdfBytes)) { // 显式指定PDF的MIME类型,避免自动识别错误 Attachment data = new Attachment(strm, fileName, "application/pdf"); // 显式指定附件编码为Base64,适配二进制文件传输 data.TransferEncoding = System.Net.Mime.TransferEncoding.Base64; oMail.Attachments.Add(data); oMail.From = new MailAddress(from); oMail.To.Add(new MailAddress(to)); oMail.Subject = subject; oMail.Body = body; oMail.BodyTransferEncoding = System.Net.Mime.TransferEncoding.Base64; using (SmtpClient oServer = new SmtpClient("XXX.XXX.XX.XXX")) { oServer.Port = **; oServer.EnableSsl = false; // 启用国际邮件格式,避免ASCII格式截断二进制内容 oServer.DeliveryFormat = SmtpDeliveryFormat.International; oServer.Send(oMail); } }
额外排查动作
- 跑通修正后的代码后先看日志里的下载字节长度,如果远小于278KB(约284672字节),优先给WebClient加上和浏览器一致的请求头、鉴权Cookie/Token,确保拿到的是完整的PDF文件而不是错误页面
- 如果手动构造MemoryStream写入内容,写完后一定要把
Position属性设为0,不然附件读取时会从流的末尾开始读,只能拿到极短的空内容 - 发完邮件后可以先在发件箱的已发送文件夹查看附件大小,如果已发送的附件大小就不对,说明是发送端构造邮件的问题;如果已发送的附件正常,收件端收到的不对,就检查中间邮件网关的附件大小、格式过滤规则
内容的提问来源于stack exchange,提问作者Damkulul
相关产品推荐
相关产品推荐

