.NET中SQL存储PDF后邮件发送背景图丢失问题排查
PDF存入SQL Server后邮件发送丢失背景图的解决方法
问题场景
开发Web应用时,将jsPDF生成的PDF文件存入SQL Server的varbinary(MAX)字段,后续取出并通过邮件发送。当前代码可正常发送PDF,但打开后背景图丢失;直接使用IFormFile发送PDF时背景图显示正常,问题出在PDF与二进制格式的转换过程。
错误原因
- 上传阶段:使用
StreamReader读取二进制PDF文件为字符串,再通过UTF8编码转换为字节数组。PDF是二进制文件,包含非UTF8兼容的字节,文本流读取会损坏原始二进制数据,导致背景图等内容丢失。 - 读取阶段:将数据库取出的字节数组转为UTF8字符串,再转回字节数组,二次破坏了二进制数据。
修正后的代码
前端AJAX提交代码(无需修改)
if(email) { var file = doc.output('blob'); var fd = new FormData(); fd.append('pdfFile',file); fd.append('jobNo',$('#jobNoInput').val()); $.ajax({ url: "/Jobs/SendInvoiceEmailToQueue", data: fd, dataType: 'text', processData: false, contentType: false, type: 'POST', success: function(data) { var data2 = JSON.parse(data); if(data2 == "SUCCESS") { quickToast('Invoice added to Email Queue for Job No: ' + $('#jobNoInput').val(), 'success', 'Added to Email Queue'); } } }); } else { doc.save('Invoice.pdf'); }
后端上传PDF至数据库代码(修正版)
public IActionResult SendInvoiceEmailToQueue(string jobNo) { IFormFile pdfFile = Request.Form.Files[0]; // 直接读取PDF文件流为字节数组,避免文本转换 byte[] pdfBytes; using (var ms = new MemoryStream()) { pdfFile.CopyTo(ms); pdfBytes = ms.ToArray(); } connectionString(); using (var con = new SqlConnection(_connectionString)) // 假设connectionString()返回连接字符串 { con.Open(); var query = "INSERT INTO dbo.email_queue([JOBNO],[PDF],[EMAILADDRESS],[DATE],[TIME]) VALUES(@jobNo, @pdf, 'address', GETDATE(), CURRENT_TIMESTAMP)"; using (var cmd = new SqlCommand(query, con)) { // 字段为varbinary(MAX),使用SqlDbType.VarBinary而非Image var picparameter = new SqlParameter("@pdf", SqlDbType.VarBinary, -1) { Value = pdfBytes }; var jobNoparameter = new SqlParameter("@jobNo", SqlDbType.Int) { Value = Int32.Parse(jobNo) }; cmd.Parameters.Add(picparameter); cmd.Parameters.Add(jobNoparameter); cmd.ExecuteNonQuery(); } } return new JsonResult("SUCCESS"); }
后端读取PDF并发送邮件代码(修正版)
public IActionResult SendInvoiceEmail(string queueNo) { connectionString(); using (var con = new SqlConnection(_connectionString)) { con.Open(); // 使用参数化查询避免SQL注入 var query = "SELECT PDF FROM dbo.recovery_email_queue WHERE EmailQueueRecNo = @queueNo"; using (var cmd = new SqlCommand(query, con)) { cmd.Parameters.AddWithValue("@queueNo", queueNo); using (var dr = cmd.ExecuteReader()) { while (dr.Read()) { // 直接取出二进制字节数组,无需UTF8转换 var pdfBytes = (byte[])dr["PDF"]; var stream = new MemoryStream(pdfBytes); // 直接创建Attachment,无需包装成IFormFile var attachment = new Attachment(stream, "invoice.pdf", "application/pdf"); using (MailMessage mail = new MailMessage()) { mail.From = new MailAddress("fromAdd"); mail.To.Add("add"); mail.Subject = "Test Sending Invoice"; mail.Body = "<h1>This is body</h1>"; mail.IsBodyHtml = true; mail.Attachments.Add(attachment); using (SmtpClient smtp = new SmtpClient("smtp-mail.outlook.com", 587)) { smtp.DeliveryMethod = SmtpDeliveryMethod.Network; smtp.UseDefaultCredentials = false; smtp.Credentials = new System.Net.NetworkCredential( ConfigurationManager.AppSettings["emailUser"].ToString(), ConfigurationManager.AppSettings["emailPass"].ToString() ); smtp.EnableSsl = true; smtp.Send(mail); } } } } } } return new JsonResult("SUCCESS"); }
额外注意事项
- 数据库字段类型为
varbinary(MAX),对应SqlDbType应使用SqlDbType.VarBinary(设置长度为-1表示MAX),而非已废弃的SqlDbType.Image。 - 所有数据库连接、命令、流对象都应使用
using语句,确保资源自动释放,避免内存泄漏。 - 读取数据库时使用参数化查询,避免SQL注入风险。
内容的提问来源于stack exchange,提问作者TryingAgain
相关产品推荐
相关产品推荐

