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


113

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

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

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

Answers:


104

这是示例代码。

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);
赞恩·阿里

3
您需要使用System.Net.Mime.MediaTypeNames.Application.Pdf
Waqas Raja

5
writer.Disopose()对于我的解决方案来说还为时过早,但其他所有示例都是很好的例子。
卡兹米尔兹

7
我很确定ms.Position = 0;在创建附件之前应该有一个。
肯尼·埃维特

94

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

这是用于发送内存中字符串作为电子邮件附件(在这种情况下为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)。


30
对于任何新手来说,我的关键是将stream.position = 0;
mtbennett

3
+1可以适当地使用using()-在线示例和摘要中似乎总是缺少某些东西(包括对此问题的公认答案)。
杰伊·奎里多

2
感谢@mtbennet,这也是我解决问题的方法stream.Position=0
伊卡洛斯

设置MIME类型实际上在这里做什么?电子邮件客户端应用有什么用吗?
xr280xr

@ xr280xr-正确。此参数实际上不是必需的,但包括该参数应有助于收件人的电子邮件客户端明智地处理附件。 docs.microsoft.com/en-us/dotnet/api/...
宁静的小湖

28

由于我在任何地方都找不到对此的确认,因此我测试了处理MailMessage和/或Attachment对象是否可以按预期发生的方式处理加载到其中的流。

在以下测试中确实显示出,当处理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);

该死的,那很聪明。一行代码,您可以将图像文件作为附件添加到电子邮件中。大提示!
Mike Gledhill 2015年

我希望这实际上已记录在案。您对此行为的测试/验证很有用,但是如果没有正式文档,很难相信这种情况会一直存在。无论如何,感谢您的测试。
卢卡斯

努力与研究@thymine
vibs2006

1
如果参考来源很重要,它已被记录在案。mailmessage.dispose调用attachments.dispose依次调用在每个附件处置,这些附件在mimepart中关闭流
Cee McSharpface

谢谢。我当时认为邮件消息应该这样做并且需要这样做,因为我在与实际发送邮件的位置不同的类中创建了我的邮件。
xr280xr

20

如果您实际上要添加.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上花费数小时,这就像一个魅力!
dijam '16

12

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

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

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

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


2

之所以落在这个问题上,是因为我需要附加通过代码生成的Excel文件,并且该文件可以作为MemoryStream。我可以将其附加到邮件中,但是它以64字节文件的形式发送,而不是原来的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让我发送带有正确附件大小的邮件的值。


2

使用其他OPEN内存流:

lauch pdf的示例,并在MVC4 C#控制器中发送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; 是帮助我的那条线。没有它,我的攻击力只是一些kB的504字节插入
Abdul Hameed

-6

我认为这段代码将帮助您:

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();
    }
  }
}

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

attachFile流也未处置
Sameh
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.