Как добавить вложения в электронную почту .NET SmtpClient MailMessage из отправки формы - PullRequest
0 голосов
/ 18 апреля 2019

У меня есть форма 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));                  
            }
        }

В настоящее время происходит генерация электронного письма и прикрепление файлов. Размер файла является правильным во вложении электронной почты (если не на несколько КБ больше, чем оригинал). Однако при попытке открыть файл я получаю сообщение о том, что он поврежден. Тестовый файл - это файл .docx. Я проверил оригинальный файл, чтобы убедиться, что он не поврежден, и я могу открыть его, поэтому я знаю, что это не файл. Я уверен, что упускаю что-то глупое. Просто нужно небольшое руководство.

UPDATE

Проблема только с файлами docx. Pdf и doc файлы в порядке. Я не уверен, почему только файл docx поврежден. Есть идеи?

1 Ответ

0 голосов
/ 18 апреля 2019

Вы используете потоки неправильно. Вам нужно использовать отдельный Stream для каждого Attachment.

В качестве хитрого трюка (я полагаю) вам не нужен промежуточный поток или буфер - но вы можете передавать потоки выгрузки файлов непосредственно конструкторам Attachment , если MailMessage будет отправлено до завершения жизненного цикла запросов / ответов ASP.NET . (Обратите внимание, что 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 );
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...