如何从表单提交中向.NET SmtpClient MailMessage电子邮件添加文件附件

问题描述 投票:0回答:1

我有一个MVC表单,上面有3个文件输入字段。如果这些输入字段具有值,我想将它们添加为通知电子邮件的附件。请注意,在下面的示例中,addedFilesHttpFileCollectionBase

        var smtpServer = Sitecore.Configuration.Settings.GetSetting("MailServer");
        var smtpPort = Sitecore.Configuration.Settings.GetSetting("MailServerPort");
        using (var stream = new MemoryStream())
        using (var mailClient = new SmtpClient(smtpServer, Convert.ToInt16(smtpPort)))
        using (var emailMessage = new MailMessage(fromAddress, toAddress, subject, message))
        {
            if (addedFiles != null && addedFiles.Count > 0)
            {
                //for some reason, the first file field was getting repeated at the end.  Workaround.
                for (int i = 0; i < 3; i++)
                {
                    string fileName = addedFiles.Keys[i];
                    HttpPostedFileBase file = addedFiles[fileName];
                    if ((file.FileName.Contains(".pdf") ||
                        file.FileName.Contains(".doc")) && file.ContentLength > 0 && file.ContentLength < 10485760)
                    {

                        var fStream = file.InputStream;
                        fStream.Position = 0;
                        fStream.CopyTo(stream);
                        var s = stream.ToArray();
                        stream.Write(s, 0, file.ContentLength);
                        stream.Position = 0; 

                        emailMessage.Attachments.Add(new Attachment(stream, file.FileName));


                    }
                }
                    await Task.Run(() => mailClient.Send(emailMessage));                  
            }
        }

目前正在发生的是生成电子邮件并附加文件。电子邮件附件中文件的大小是正确的(如果不比原始文件大几KB)。但是,在尝试打开文件时,我收到一条消息,说明它已损坏。测试文件是.docx文件。我已经测试了原始文件,以确保它没有损坏,我可以打开它,所以我知道它不是文件。我确定我错过了一些愚蠢的话。只需要一点指导。

UPDATE

问题仅出在docx文件中。 Pdf和doc文件很好。我不确定为什么只有docx文件通过损坏才能通过。有任何想法吗?

c# attachment smtpclient
1个回答
0
投票

您错误地使用了Streams。你需要为每个Stream使用单独的Attachment

作为一个巧妙的技巧,(我相信)你不需要中间流或缓冲区 - 但你可以将文件上传流直接传递给Attachment构造函数,前提是在ASP.NET请求/响应生命周期结束之前将发送MailMessage 。 (请注意,Attachment获取传递给其构造函数的流的所有权,因此如果父MailMessage也被处置,则您不需要自己处理附件的流)。

还有一些东西在你的代码中看起来不正确(例如硬编码3的文件数量)和await Task.Run( ... )用于非异步操作。

当您使用System.Web版本的ASP.NET(即不使用ASP.NET Core)时,我不建议使用任何async API,因为这会混淆请求/响应生命周期。

试试这个:

HttpFileCollectionBase addedFiles = ...
using( SmtpClient  mailClient = new SmtpClient( smtpServer, Convert.ToInt16( smtpPort ) ) )
using( MailMessage emailMessage = new MailMessage( fromAddress, toAddress, subject, message ) )
{
    if( addedFiles?.Count > 0 )
    {
        foreach( HttpPostedFileBase file in addedFiles )
        {
            Boolean isOK = ( file.FileName.EndsWith( ".pdf", StringComparison.OrdinalIgnoreCase ) || file.FileName.EndsWith( ".doc", StringComparison.OrdinalIgnoreCase ) ) && file.ContentLength > 0 && file.ContentLength < 10485760;
            if( isOK )
            {
                Attachment att = new Attachment( file.InputStream, name: file.FileName );
                emailMessage.Attachments.Add( att );
            } 
        }
    }

    mailClient.Send( emailMessage );
}

如果你确实需要让MailMessage比ASP.NET请求/响应生命周期更长,或者如果你想在附加它们之前检查或处理上传的文件,那么你需要单独缓冲它们,如下所示:

HttpFileCollectionBase addedFiles = ...
using( SmtpClient  mailClient = new SmtpClient( smtpServer, Convert.ToInt16( smtpPort ) ) )
using( MailMessage emailMessage = new MailMessage( fromAddress, toAddress, subject, message ) )
{
    if( addedFiles?.Count > 0 )
    {
        foreach( HttpPostedFileBase file in addedFiles )
        {
            Boolean isOK = ( file.FileName.EndsWith( ".pdf", StringComparison.OrdinalIgnoreCase ) || file.FileName.EndsWith( ".doc", StringComparison.OrdinalIgnoreCase ) ) && file.ContentLength > 0 && file.ContentLength < 10485760;
            if( isOK )
            {
                MemoryStream copy = new MemoryStream( capacity: file.ContentLength );
                file.InputStream.CopyTo( copy );
                // Rewind the stream, this is important! (You cannot rewind ASP.NET's file.InputStream, hence why we use a MemoryStream copy).
                copy.Seek( 0, SeekOrigin.Begin );

                DoSomethingWithFileStream( copy );

                // Rewind the stream again, this is important!
                copy.Seek( 0, SeekOrigin.Begin );

                Attachment att = new Attachment( copy, name: file.FileName );
                emailMessage.Attachments.Add( att );
            } 
        }
    }

    mailClient.Send( emailMessage );
}
© www.soinside.com 2019 - 2024. All rights reserved.