如何将Controller生成的MimePart变量传递到Blazor页面代码隐藏类?
问题根源
你当前的实现思路存在架构逻辑错误:MimePart是MimeKit生成的进程内内存对象,生命周期仅存在于Controller单次请求的处理过程中,请求结束后相关内存资源就会被释放,不存在可以直接跨请求传递到Blazor端的对象引用,强行用静态变量、临时缓存存这类对象还会引发并发冲突、内存泄漏、多用户数据串流的问题,完全不具备可行性。
你不需要实现「Controller生成MimePart再传回Blazor」的逻辑,根据你用的Blazor托管模式选下面两种方案即可:
方案1:直接在Blazor侧生成MimePart(适合Blazor Server模式,实现最简单)
Blazor本身就能直接拿到用户选中的上传文件,完全不需要绕一层Controller生成MimePart,直接在SendMail方法里读取文件生成附件即可:
- 先在页面代码隐藏类里加变量存用户选中的文件,绑定文件选择组件的变更事件:
// 限制最大上传文件数,按需调整 private const int MaxFileCount = 10; // 单文件大小上限,这里设为10MB,按需调整 private const long MaxFileSize = 10 * 1024 * 1024; private IReadOnlyList<IBrowserFile> _selectedFiles = Array.Empty<IBrowserFile>(); private void OnFileSelected(InputFileChangeEventArgs e) { _selectedFiles = e.GetMultipleFiles(MaxFileCount); }
- 改造原有的
SendMail方法,直接遍历选中的文件生成MimePart加入邮件体:
private async Task SendMail() { try { var email = new MimeMessage(); email.From.Add(MailboxAddress.Parse(sender)); email.To.Add(MailboxAddress.Parse(receiver)); email.Subject = emailSubject; var multipart = new Multipart("mixed"); multipart.Add(new TextPart(TextFormat.Html) { Text = emailMessage }); // 直接遍历选中文件生成附件,无需走Controller foreach (var file in _selectedFiles) { var memoryStream = new MemoryStream(); await using var uploadStream = file.OpenReadStream(MaxFileSize); await uploadStream.CopyToAsync(memoryStream); memoryStream.Position = 0; var contentType = ContentType.Parse(file.ContentType); var attachmentPart = new MimePart(contentType.MimeType) { FileName = file.Name, ContentTransferEncoding = ContentEncoding.Base64, Content = new MimeContent(memoryStream), }; multipart.Add(attachmentPart); } email.Body = multipart; using var smtp = new SmtpClient(); smtp.Connect(outgoingServer, outgoingPort, SecureSocketOptions.Auto); smtp.Authenticate(userName, userPassword); smtp.Send(email); smtp.Disconnect(true); } catch (Exception ex) { NotificationService.Notify(NotificationSeverity.Error, "Send Email Error!", ex.Message, 7000); } }
注意:Blazor WebAssembly模式不要用这个方案,SMTP的账号、密码、服务器地址会打包到前端程序集泄露给所有用户,这种场景用方案2。
方案2:合并上传与发信逻辑到Controller(适合Blazor WebAssembly模式,安全性更高)
如果是WASM模式,所有SMTP相关逻辑必须放在服务端,不要拆分「上传文件」和「发送邮件」两个独立接口,直接写一个统一接口接收文件和邮件参数,在Controller内部完成MimePart生成、邮件组装、发送全流程,根本不需要把MimePart传回Blazor:
- 先定义接口接收的参数模型:
public class SendMailRequest { public string Sender { get; set; } public string Receiver { get; set; } public string Subject { get; set; } public string HtmlContent { get; set; } }
- 改造Controller,合并原有上传和发信逻辑:
[HttpPost("mail/send-with-attachments")] public async Task<IActionResult> SendWithAttachments([FromForm] SendMailRequest request, [FromForm] IFormFile[] files) { try { var email = new MimeMessage(); email.From.Add(MailboxAddress.Parse(request.Sender)); email.To.Add(MailboxAddress.Parse(request.Receiver)); email.Subject = request.Subject; var multipart = new Multipart("mixed"); multipart.Add(new TextPart(TextFormat.Html) { Text = request.HtmlContent }); foreach (var file in files) { var contentStream = new MemoryStream(); await file.CopyToAsync(contentStream); contentStream.Position = 0; var contentType = ContentType.Parse(file.ContentType); var part = new MimePart(contentType.MimeType) { FileName = Path.GetFileName(file.FileName), ContentTransferEncoding = ContentEncoding.Base64, Content = new MimeContent(contentStream), }; multipart.Add(part); } email.Body = multipart; // SMTP配置建议从配置文件读取,不要硬编码 using var smtp = new SmtpClient(); smtp.Connect(outgoingServer, outgoingPort, SecureSocketOptions.Auto); smtp.Authenticate(userName, userPassword); smtp.Send(email); smtp.Disconnect(true); return Ok(); } catch (Exception ex) { return StatusCode(500, ex.Message); } }
- Blazor端只需要用
MultipartFormDataContent把邮件参数和选中的文件一起提交到这个接口即可,不需要处理任何MimePart和SMTP逻辑。
内容的提问来源于stack exchange,提问作者David Gammon
相关产品推荐
相关产品推荐

