我有一个MVC表单,上面有3个文件输入字段。如果这些输入字段具有值,我想将它们添加为通知电子邮件的附件。请注意,在下面的示例中,addedFiles
是HttpFileCollectionBase
。
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文件通过损坏才能通过。有任何想法吗?
您错误地使用了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 );
}