将文件从 MemoryStream 附加到 C# 中的 MailMessage

Posted

技术标签:

【中文标题】将文件从 MemoryStream 附加到 C# 中的 MailMessage【英文标题】:Attach a file from MemoryStream to a MailMessage in C# 【发布时间】:2011-07-17 05:07:07 【问题描述】:

我正在编写一个将文件附加到电子邮件的程序。目前我正在使用FileStream 将文件保存到磁盘中,然后我使用

System.Net.Mail.MailMessage.Attachments.Add(
    new System.Net.Mail.Attachment("file name")); 

我不想将文件存储在磁盘中,我想将文件存储在内存中,然后从内存流中将其传递给Attachment

【问题讨论】:

【参考方案1】:

这里是示例代码。

System.IO.MemoryStream ms = new System.IO.MemoryStream();
System.IO.StreamWriter writer = new System.IO.StreamWriter(ms);
writer.Write("Hello its my sample file");
writer.Flush();
writer.Dispose();
ms.Position = 0;

System.Net.Mime.ContentType ct = new System.Net.Mime.ContentType(System.Net.Mime.MediaTypeNames.Text.Plain);
System.Net.Mail.Attachment attach = new System.Net.Mail.Attachment(ms, ct);
attach.ContentDisposition.FileName = "myFile.txt";

// I guess you know how to send email with an attachment
// after sending email
ms.Close();

编辑 1

您可以通过 System.Net.Mime.MimeTypeNames 指定其他文件类型,例如 System.Net.Mime.MediaTypeNames.Application.Pdf

根据 Mime 类型,您需要在 FileName 中指定正确的扩展名,例如 "myFile.pdf"

【讨论】:

我正在使用 PDF 如何将此类型传递给 System.Net.Mime.ContentType ct = new System.Net.Mime.ContentType(System.Net.Mime.MediaTypeNames.Text.Plain); 你需要使用System.Net.Mime.MediaTypeNames.Application.Pdf writer.Disopose() 对我的解决方案来说还为时过早,但其他所有的都是很好的例子。 我很确定在创建附件之前应该有一个ms.Position = 0;【参考方案2】:

有点晚了-但希望对那里的人仍然有用:-

这是一个简化的 sn-p,用于将内存中的字符串作为电子邮件附件(在本例中为 CSV 文件)发送。

using (var stream = new MemoryStream())
using (var writer = new StreamWriter(stream))    // using UTF-8 encoding by default
using (var mailClient = new SmtpClient("localhost", 25))
using (var message = new MailMessage("me@example.com", "you@example.com", "Just testing", "See attachment..."))

    writer.WriteLine("Comma,Seperated,Values,...");
    writer.Flush();
    stream.Position = 0;     // read from the start of what was written

    message.Attachments.Add(new Attachment(stream, "filename.csv", "text/csv"));

    mailClient.Send(message);

在发送消息之前不应释放 StreamWriter 和底层流(以避免ObjectDisposedException: Cannot access a closed Stream)。

【讨论】:

对于任何新手来说,我的关键是设置stream.position = 0; +1 用于适当使用 using() —— 在线示例和 sn-ps 中似乎总是缺少的东西(包括这个问题的公认答案)。 感谢@m.t.bennet 这也是我的问题 - 设置stream.Position=0 在这里设置 MIME 类型实际上做了什么?电子邮件客户端应用程序的东西? @xr280xr - 正确。此参数实际上不是必需的,但包含它应该有助于收件人的电子邮件客户端明智地处理附件。 docs.microsoft.com/en-us/dotnet/api/…【参考方案3】:

由于我无法在任何地方找到对此的确认,我测试了处置 MailMessage 和/或附件对象是否会像我预期的那样处置加载到其中的流。

在以下测试中确实显示,当 MailMessage 被释放时,用于创建附件的所有流也将被释放。因此,只要您处理 MailMessage,创建它的流就不需要处理。

MailMessage mail = new MailMessage();
//Create a MemoryStream from a file for this test
MemoryStream ms = new MemoryStream(File.ReadAllBytes(@"C:\temp\test.gif"));

mail.Attachments.Add(new System.Net.Mail.Attachment(ms, "test.gif"));
if (mail.Attachments[0].ContentStream == ms) Console.WriteLine("Streams are referencing the same resource");
Console.WriteLine("Stream length: " + mail.Attachments[0].ContentStream.Length);

//Dispose the mail as you should after sending the email
mail.Dispose();
//--Or you can dispose the attachment itself
//mm.Attachments[0].Dispose();

Console.WriteLine("This will throw a 'Cannot access a closed Stream.' exception: " + ms.Length);

【讨论】:

该死,这很聪明。一行代码,您可以将图像文件作为附件添加到电子邮件中。很棒的提示! 我希望这确实被记录在案。您对此行为的测试/验证很有用,但如果没有在官方文档中,很难相信情况总是如此。无论如何,感谢您的测试。 努力和研究@thymine 如果引用源很重要,它会被记录下来。 mailmessage.dispose 调用 attachments.dispose,后者又调用 dispose on each attachment,在 mimepart 中,closes the stream。 谢谢。我在想邮件应该这样做并且需要这样做,因为我在与实际发送邮件的位置不同的类中创建邮件。【参考方案4】:

如果你真的想添加一个.pdf,我发现有必要将内存流的位置设置为零。

var memStream = new MemoryStream(yourPdfByteArray);
memStream.Position = 0;
var contentType = new System.Net.Mime.ContentType(System.Net.Mime.MediaTypeNames.Application.Pdf);
var reportAttachment = new Attachment(memStream, contentType);
reportAttachment.ContentDisposition.FileName = "yourFileName.pdf";
mailMessage.Attachments.Add(reportAttachment);

【讨论】:

花几个小时发送一个 pdf 文件,这简直太棒了!【参考方案5】:

如果您所做的只是附加一个字符串,您只需 2 行即可完成:

mail.Attachments.Add(Attachment.CreateAttachmentFromString("1,2,3", "text/csv");
mail.Attachments.Last().ContentDisposition.FileName = "filename.csv";

我无法通过 StreamWriter 使用我们的邮件服务器。 我想可能是因为使用 StreamWriter 您丢失了很多文件属性信息,也许我们的服务器不喜欢丢失的内容。 使用 Attachment.CreateAttachmentFromString() 它创建了我需要的一切并且效果很好!

否则,我建议您获取内存中的文件并使用 MemoryStream(byte[]) 打开它,然后一起跳过 StreamWriter。

【讨论】:

此方法不需要您在发送之前保持流打开,这对我创建电子邮件的方式非常有帮助。谢谢!【参考方案6】:

我遇到了这个问题,因为我需要附加一个通过代码生成的 Excel 文件,该文件以 MemoryStream 的形式提供。我可以将它附加到邮件消息中,但它是作为 64Bytes 文件发送的,而不是按原意发送的 ~6KB。所以,对我有用的解决方案是:

MailMessage mailMessage = new MailMessage();
Attachment attachment = new Attachment(myMemorySteam, new ContentType(MediaTypeNames.Application.Octet));

attachment.ContentDisposition.FileName = "myFile.xlsx";
attachment.ContentDisposition.Size = attachment.Length;

mailMessage.Attachments.Add(attachment);

设置attachment.ContentDisposition.Size 的值让我发送带有正确附件大小的邮件。

【讨论】:

【参考方案7】:

使用 OTHER OPEN 内存流:

在 MVC4 C# 控制器中 lauch pdf 和发送 pdf 的示例

        public void ToPdf(string uco, int idAudit)
    
        Response.Clear();
        Response.ContentType = "application/octet-stream";
        Response.AddHeader("content-disposition", "attachment;filename= Document.pdf");
        Response.Buffer = true;
        Response.Clear();

        //get the memorystream pdf
        var bytes = new MisAuditoriasLogic().ToPdf(uco, idAudit).ToArray();

        Response.OutputStream.Write(bytes, 0, bytes.Length);
        Response.OutputStream.Flush();

    


    public ActionResult ToMail(string uco, string filter, int? page, int idAudit, int? full) 
    
        //get the memorystream pdf
        var bytes = new MisAuditoriasLogic().ToPdf(uco, idAudit).ToArray();

        using (var stream = new MemoryStream(bytes))
        using (var mailClient = new SmtpClient("**YOUR SERVER**", 25))
        using (var message = new MailMessage("**SENDER**", "**RECEIVER**", "Just testing", "See attachment..."))
        

            stream.Position = 0;

            Attachment attach = new Attachment(stream, new System.Net.Mime.ContentType("application/pdf"));
            attach.ContentDisposition.FileName = "test.pdf";

            message.Attachments.Add(attach);

            mailClient.Send(message);
        

        ViewBag.errMsg = "Documento enviado.";

        return Index(uco, filter, page, idAudit, full);
    

【讨论】:

stream.Position=0; 是帮助我的行。没有它,我的附件只有 504 个字节,而不是一些 kBs【参考方案8】:

我认为这段代码会对你有所帮助:

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.htmlControls;
using System.Net.Mail;

public partial class _Default : System.Web.UI.Page

  protected void Page_Load(object sender, EventArgs e)
  

  

  protected void btnSubmit_Click(object sender, EventArgs e)
  
    try
    
      MailAddress SendFrom = new MailAddress(txtFrom.Text);
      MailAddress SendTo = new MailAddress(txtTo.Text);

      MailMessage MyMessage = new MailMessage(SendFrom, SendTo);

      MyMessage.Subject = txtSubject.Text;
      MyMessage.Body = txtBody.Text;

      Attachment attachFile = new Attachment(txtAttachmentPath.Text);
      MyMessage.Attachments.Add(attachFile);

      SmtpClient emailClient = new SmtpClient(txtSMTPServer.Text);
      emailClient.Send(MyMessage);

      litStatus.Text = "Message Sent";
    
    catch (Exception ex)
    
      litStatus.Text = ex.ToString();
    
  

【讨论】:

-1 此答案使用 Attachment(string fileName) 构造函数从磁盘加载附件。 OP 特别声明他不想从磁盘加载。此外,这只是从 Red Swan 的答案中的链接复制粘贴的代码。 attachFile 流也没有处理

以上是关于将文件从 MemoryStream 附加到 C# 中的 MailMessage的主要内容,如果未能解决你的问题,请参考以下文章

C# 将 zip 条目从一个 zipFile 导出到另一个 zipFile

将 MemoryStream 文件存储到 Azure Blob

c# 在内存打包zip 多个文件 上传服务器 MemoryStream ZipArchiveMode

从 MemoryStream 获取子字符串而不将整个流转换为字符串

c#无法将文本附加到现有文件[关闭]

附加到 MailMessage 对象时的 C# 空文件