Attach FileAttachment to message.Attachments in c# EWS - c#

I am trying to add a FileAttachment to a message. Unfortunately message.Attachments.AddFileAttachment() asks for a path, which I don't have. I have the FileAttachment straight from another mail.
Here I get the attachment:
EmailMessage oldEmailMessage = EmailMessage.Bind(service, olderSoxMail.Id);
// load attachments
FileAttachment fileAttachment = newEmailMessage.Attachments[0] as FileAttachment;
fileAttachment.Load();
Here I try to attach the attachment to a mail
public static void ComposeEmail(string recipient, string subject, string body, FileAttachment[] attachment = null)
{
var map = new Dictionary<char, string>() {
{ 'ä', "ae" },
{ 'ö', "oe" },
{ 'ü', "ue" },
{ 'Ä', "Ae" },
{ 'Ö', "Oe" },
{ 'Ü', "Ue" },
{ 'ß', "ss" }
};
recipient = recipient.Aggregate(
new StringBuilder(),
(sb, c) =>
{
string r;
if (map.TryGetValue(c, out r))
return sb.Append(r);
else
return sb.Append(c);
}).ToString();
EmailMessage message = new EmailMessage(service);
message.Subject = subject;
message.Body = body;
message.ToRecipients.Add(recipient);
foreach (FileAttachment attach in attachment)
{
// COMPILER ERROR HERE: Cannot Convert from FileAttachment to Path (string)
message.Attachments.AddFileAttachment(attach);
// END ERROR
}
message.SendAndSaveCopy();
}
Do you know how I can add the FileAttachment?

Now your fileattachments that you are receiving exist on the Exchange server. You can download those attachments and store the path and attach those paths to your new email.
A better solution is reading the attachments as streams and attach them directly to the mail without saving them to disk first.
foreach (var fileAttachment in fileAttachments)
{
var stream = new System.IO.MemoryStream(fileAttachment.Content);
message.Attachments.AddFileAttachment(fileAttachment.Name, stream);
}

Related

Send Generated Pdf as Email Attachment Asp.NetCore

I am using Rotativa to convert my view to pdf. I would like to send that generated pdf as an email attachment (without having to download it first to disk). I've been following a bunch of tutorials to do this but I just keep going round in circles. I would much appreciate any help I can get.
public async Task<IActionResult>SomeReport()
{
...
return new ViewAsPdf (report)
}
return view();
MemoryStream memoryStream = new MemoryStream();
MimeMessage msg = new MimeMessage();
MailboxAddress from = new MailboxAddress ("Name", "emailAddress")
msg.From.Add(from);
MailboxAddress to = new MailboxAddress ("Name", "emailAddress")
msg.From.Add(to);
BodyBuilder bd = new BodyBuilder();
bb.HtmlBody ="some text";
bb.Attachments.Add("attachmentName", new MemoryStream());
msg.Body = bb.ToMessageBody();
SmtpClient smtp = new SmtpClient();
smtp.Connect("smtp.gmail.com",465, true);
smtp.Authenticate("emailAddress", "Pwd");
smtp.Send(msg);
smtp.Disconnect(true);
smtp.Dispose();
Edit
Parent View from which Email is sent
#Model MyProject.Models.EntityViewModel
<a asp-action= "SendPdfMail" asp-controller ="Student" asp-route-id = "#Model.Student.StudentId">Email</a>
...
SendPdfMail action in Student Controller
public async Task<IActionResult> SendPdfMail(string id)
{
var student = await context.Student. Where(s => s.StudentId == id);
if (student != null)
{
...
var viewAsPdf = new ViewAsPdf("MyPdfView", new{route = id})
{
Model = new EntityViewModel(),
FileName = PdfFileName,
...
}
}
};
Complete answer using Rotativa.AspNetCore. Code is developed in VS 2019, Core 3.1, Rotativa.AspNetCore 1.1.1.
Nuget
Install-package Rotativa.AspNetCore
Sample controller
public class SendPdfController : ControllerBase
{
private const string PdfFileName = "test.pdf";
private readonly SmtpClient _smtpClient;
public SendPdfController(SmtpClient smtpClient)
{
_smtpClient = smtpClient;
}
[HttpGet("SendPdfMail")] // https://localhost:5001/SendPdfMail
public async Task<IActionResult> SendPdfMail()
{
using var mailMessage = new MailMessage();
mailMessage.To.Add(new MailAddress("a#b.c"));
mailMessage.From = new MailAddress("c#d.e");
mailMessage.Subject = "mail subject here";
var viewAsPdf = new ViewAsPdf("view name", <YOUR MODEL HERE>)
{
FileName = PdfFileName,
PageSize = Size.A4,
PageMargins = { Left = 1, Right = 1 }
};
var pdfBytes = await viewAsPdf.BuildFile(ControllerContext);
using var attachment = new Attachment(new MemoryStream(pdfBytes), PdfFileName);
mailMessage.Attachments.Add(attachment);
_smtpClient.Send(mailMessage); // _smtpClient will be disposed by container
return new OkResult();
}
}
Options class
public class SmtpOptions
{
public string Host { get; set; }
public int Port { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
In Startup#ConfigureServices
services.Configure<SmtpOptions>(Configuration.GetSection("Smtp"));
// SmtpClient is not thread-safe, hence transient
services.AddTransient(provider =>
{
var smtpOptions = provider.GetService<IOptions<SmtpOptions>>().Value;
return new SmtpClient(smtpOptions.Host, smtpOptions.Port)
{
// Credentials and EnableSsl here when required
};
});
appsettings.json
{
"Smtp": {
"Host": "SMTP HOST HERE",
"Port": PORT NUMBER HERE,
"Username": "USERNAME HERE",
"Password": "PASSWORD HERE"
}
}
There's not quite enough to go on, but you need something like this:
MimeMessage msg = new MimeMessage();
MailboxAddress from = new MailboxAddress ("Name", "emailAddress");
msg.From.Add(from);
MailboxAddress to = new MailboxAddress ("Name", "emailAddress");
msg.To.Add(to);
BodyBuilder bb = new BodyBuilder();
bb.HtmlBody ="some text";
using (var wc = new WebClient())
{
bb.Attachments.Add("attachmentName",wc.DownloadData("Url for your view goes here"));
}
msg.Body = bb.ToMessageBody();
using (var smtp = new SmtpClient())
{
smtp.Connect("smtp.gmail.com",465, true);
smtp.Authenticate("emailAddress", "Pwd");
smtp.Send(msg);
smtp.Disconnect(true);
}
Notice this adds the attachment before calling .ToMessageBody(), as well as fixing at least four basic typos.
But I doubt this will be enough... I suspect ViewAsPdf() needs more context than you get from a single DownloadData() request, and you'll need to go back to the drawing board to be able to provide that context, but this at least will help push you in the right direction.

ArgumentNullException for address when sending email using NetCore.MailKit

I am using NetCore.MailKit NuGet package to help me send an email which contains a link to confirm their email when the user registers. I have followed the steps on their Github page but, I am getting an ArgumentNullException for the address, even though I have set the sending address.
The error:
ArgumentNullException: Value cannot be null. (Parameter 'address')
MimeKit.MailboxAddress..ctor(Encoding encoding, string name, string address)
The above error occurs when is send the email in my controller using IEmailService:
_EmailService.Send("usersemail#gmail.com", "subject", "message body");
This is my appsettings.json configuration:
"EmailConfiguration": {
"Server": "smtp.gmail.com",
"Port": 587,
"SenderName": "name",
"SenderEmail": "mygmail#gmail.com",
"SenderPassword": "My gmail password"
}
This is my setup in startup.cs
services.AddMailKit(optionBuilder =>
{
optionBuilder.UseMailKit(new MailKitOptions()
{
//get options from sercets.json
Server = Configuration["Server"],
Port = Convert.ToInt32(Configuration["Port"]),
SenderName = Configuration["SenderName"],
SenderEmail = Configuration["SenderEmail"],
Account = Configuration["SenderEmail"],
Password = Configuration["SenderPassword"],
Security = true
});
});
Below is the code for EmailService:
public void Send(string mailTo, string subject, string message, bool isHtml = false)
{
SendEmail(mailTo, null, null, subject, message, Encoding.UTF8, isHtml);
}
private void SendEmail(string mailTo, string mailCc, string mailBcc, string subject, string message, Encoding encoding, bool isHtml)
{
var _to = new string[0];
var _cc = new string[0];
var _bcc = new string[0];
if (!string.IsNullOrEmpty(mailTo))
_to = mailTo.Split(',').Select(x => x.Trim()).ToArray();
if (!string.IsNullOrEmpty(mailCc))
_cc = mailCc.Split(',').Select(x => x.Trim()).ToArray();
if (!string.IsNullOrEmpty(mailBcc))
_bcc = mailBcc.Split(',').Select(x => x.Trim()).ToArray();
Check.Argument.IsNotEmpty(_to, nameof(mailTo));
Check.Argument.IsNotEmpty(message, nameof(message));
var mimeMessage = new MimeMessage();
//add mail from
mimeMessage.From.Add(new MailboxAddress(_MailKitProvider.Options.SenderName, _MailKitProvider.Options.SenderEmail));
//add mail to
foreach (var to in _to)
{
mimeMessage.To.Add(MailboxAddress.Parse(to));
}
//add mail cc
foreach (var cc in _cc)
{
mimeMessage.Cc.Add(MailboxAddress.Parse(cc));
}
//add mail bcc
foreach (var bcc in _bcc)
{
mimeMessage.Bcc.Add(MailboxAddress.Parse(bcc));
}
//add subject
mimeMessage.Subject = subject;
//add email body
TextPart body = null;
if (isHtml)
{
body = new TextPart(TextFormat.Html);
}
else
{
body = new TextPart(TextFormat.Text);
}
//set email encoding
body.SetText(encoding, message);
//set email body
mimeMessage.Body = body;
using (var client = _MailKitProvider.SmtpClient)
{
client.Send(mimeMessage);
}
}
As you can see I have set everything, am I missing something here? why is address null at MimeKit.MailboxAddress()?
You seem to be not loading your configuration settings correctly. I suspect the failing line in your code is
//add mail from
mimeMessage.From.Add(new MailboxAddress(_MailKitProvider.Options.SenderName, _MailKitProvider.Options.SenderEmail));
The Options are all null, which causes the exception.
When you load the settings from a configuration file you need to prepend the section name to the variables. E.G.
services.AddMailKit(optionBuilder =>
{
optionBuilder.UseMailKit(new MailKitOptions()
{
//get options from sercets.json
Server = Configuration["EmailConfiguration:Server"],
Port = Convert.ToInt32(Configuration["EmailConfiguration:Port"]),
SenderName = Configuration["EmailConfiguration:SenderName"],
SenderEmail = Configuration["EmailConfiguration:SenderEmail"],
Account = Configuration["EmailConfiguration:SenderEmail"],
Password = Configuration["EmailConfiguration:SenderPassword"],
Security = true
});
});
My guess is that the exception is being thrown on the following line:
mimeMessage.From.Add(new MailboxAddress(_MailKitProvider.Options.SenderName, _MailKitProvider.Options.SenderEmail));
This means that _MailKitProvider.Options.SenderEmail is null.
I know you expect these values to be loaded correctly from your appsettings.json file, but seemingly, they are not being loaded for some reason.

MailItem attachment unknown

I have a mail containing only a signature as an image and an attachment like the screenshot below.
I save this email as C:\mail.msg, I try then to read it by the code below:
var oApp = new Microsoft.Office.Interop.Outlook.Application();
MailItem outlookMsg = (Microsoft.Office.Interop.Outlook.MailItem)oApp.CreateItemFromTemplate(#"C:\mail.msg");
//there are 2 attachments inside
foreach(var att in outlookMsg.Attachments)
{
att.SaveAsFile($#"C:\{att.FileName}");
}
The problem
There are 2 attachments inside the MailItem named :
-empty.xlsx
-lot4.xlsx
If I change the extension of lot4.xlsx to lot4.png, it can be opened as the image in the signature.
Someone has seen this strange situation when an attachment is added with name incorrect?
You could download attachments using the below code:
private void ThisApplication_NewMail()
{
Outlook.MAPIFolder inBox = this.Application.ActiveExplorer()
.Session.GetDefaultFolder(Outlook
.OlDefaultFolders.olFolderInbox);
Outlook.Items inBoxItems = inBox.Items;
Outlook.MailItem newEmail = null;
inBoxItems = inBoxItems.Restrict("[Unread] = true");
try
{
foreach (object collectionItem in inBoxItems)
{
newEmail = collectionItem as Outlook.MailItem;
if (newEmail != null)
{
if (newEmail.Attachments.Count > 0)
{
for (int i = 1; i <= newEmail
.Attachments.Count; i++)
{
newEmail.Attachments[i].SaveAsFile
(#"C:\TestFileSave\" +
newEmail.Attachments[i].FileName);
}
}
}
}
}
catch (Exception ex)
{
string errorInfo = (string)ex.Message
.Substring(0, 11);
if (errorInfo == "Cannot save")
{
MessageBox.Show(#"Create Folder C:\TestFileSave");
}
}
}
For more information, please refer to this link:
How to: Programmatically save attachments from Outlook email items
With Microsoft EWS, its very easy to do:
reference: http://johnlabtest.blogspot.com/2014/01/save-attachments-from-exchange-mail-box.html
static void Main(string[] args)
{
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
service.Credentials = new WebCredentials("user1#contoso.com", "password");
service.TraceEnabled = true;
service.TraceFlags = TraceFlags.All;
service.AutodiscoverUrl("user1#contoso.com", RedirectionUrlValidationCallback);
var messages = new List<EmailMessage>();
// only get unread emails
SearchFilter folderSearchFilter = new SearchFilter.IsEqualTo(EmailMessageSchema.IsRead, false);
// we just need the id in our results
var itemView = new ItemView(10) {PropertySet = new PropertySet(BasePropertySet.IdOnly)};
FindItemsResults<Item> findResults = service.FindItems(folder.Id, folderSearchFilter, itemView);
foreach (Item item in findResults.Items.Where(i => i is EmailMessage))
{
EmailMessage message = EmailMessage.Bind(service, item.Id, new PropertySet(BasePropertySet.IdOnly, ItemSchema.Attachments, ItemSchema.HasAttachments));
messages.Add(message);
}
// loop through messages and call processemail here.
}
public static void ProcessEmail(EmailMessage message)
{
string saveDir = ConfigurationManager.AppSettings["AttachmentSaveDirectory"];
if (message.HasAttachments)
{
foreach (Attachment attachment in message.Attachments.Where(a=> a is FileAttachment))
{
FileAttachment fileAttachment = attachment as FileAttachment;
fileAttachment.Load(); // populate the content property of the attachment
using (FileStream fs = new FileStream(saveDir + attachment.Name, FileMode.Create))
{
using (BinaryWriter w = new BinaryWriter(fs))
{
w.Write(fileAttachment.Content);
}
}
}
}
message.IsRead = true;
message.Update(ConflictResolutionMode.AutoResolve); // push changes back to server
}
private static bool RedirectionUrlValidationCallback(string redirectionUrl)
{
// The default for the validation callback is to reject the URL.
bool result = false;
Uri redirectionUri = new Uri(redirectionUrl);
// Validate the contents of the redirection URL. In this simple validation
// callback, the redirection URL is considered valid if it is using HTTPS
// to encrypt the authentication credentials.
if (redirectionUri.Scheme == "https")
{
result = true;
}
return result;
}

How to get the Attachement File

Here I am trying to get all the part of an email separately like body, attachments, address part also So I have the below code . So can I get the attachment files also by using same technique( Using PropertySet class or define RequestedBodyType to something)???
Is there any way to get the contents of Attachment files of any type and I don't need to change the code too much??
// Get the Unread mails from the server
SearchFilter itemFilter = new SearchFilter.IsEqualTo(EmailMessageSchema.IsRead, false);
// get the emails from Inbox folder
FindItemsResults<Item> emails = service.FindItems(WellKnownFolderName.Inbox, itemFilter, view);
foreach (EmailMessage em in emails)
{
itempropertyset.RequestedBodyType = BodyType.HTML;
em.Load(itempropertyset);
em.IsRead = true;
em.Update(ConflictResolutionMode.AlwaysOverwrite);
EmailProList.HTMLBody = em.Body.Text;
itempropertyset.RequestedBodyType = BodyType.Text;
em.Load(itempropertyset);
EmailProList.Body = em.Body.Text;
itempropertyset.RequestedBodyType =
EmailProList.ToEmailAddr = em.Sender.Address.ToString(); //JG Changed
EmailProList.Subject = em.Subject.ToString();
EmailProList.Type = "Feedback";
}
You can get the attachments like this:
EmailMessage email = item as EmailMessage;
foreach(FileAttachment file in email.FileAttachments)
{
// Process the attachment
}
You can process the attachments by this way.
if (message.HasAttachments && message.Attachments[0] is FileAttachment)
{
FileAttachment fileAttachment = message.Attachments[0] as FileAttachment;
Console.WriteLine("email" + fileAttachment.Name);
fileAttachment.Load(#"D:\\" + fileAttachment.Name);
}

Sending Mails with attachment in C#

I need to send a mail including the exception details (Yellow Screen Of Death) as attachment.
I could get the YSOD as follows:
string YSODmarkup = lastErrorWrapper.GetHtmlErrorMessage();
if (!string.IsNullOrEmpty(YSODmarkup))
{
Attachment YSOD = Attachment.CreateAttachmentFromString(YSODmarkup, "YSOD.htm");
mm.Attachments.Add(YSOD);
}
mm is of type MailMessage, but the mail is not sent.
Here
System.Net.Mail.MailMessage MyMailMessage = new System.Net.Mail.MailMessage("from", "to", "Exception-Details", htmlEmail.ToString());
is used to bind the body of the mail.
After this only the attachment is added.
While removing the attachment, mail is sent.
Can anyone help me out?
As per the comments from Mr. Albin and Mr. Paul am updating the following
string YSODmarkup = Ex_Details.GetHtmlErrorMessage();
string p = System.IO.Directory.GetCurrentDirectory();
p = p + "\\trial.txt";
StreamWriter sw = new StreamWriter(p);
sw.WriteLine(YSODmarkup);
sw.Close();
Attachment a = new Attachment(p);
if (!string.IsNullOrEmpty(YSODmarkup))
{
Attachment YSOD = Attachment.CreateAttachmentFromString(YSODmarkup, "YSOD.html");
System.Net.Mail.Attachment(server.mappath("C:\\Documents and Settings\\user\\Desktop\\xml.docx"));
MyMailMessage.Attachments.Add(a);
}
Here i attached the contents to a text file and tried the same. So the mail was not sent. Is there any issue with sending mails which contains HTML tags in it. Because i was able to attach a normal text file.
namespace SendAttachmentMail
{
class Program
{
static void Main(string[] args)
{
var myAddress = new MailAddress("jhered#yahoo.com","James Peckham");
MailMessage message = new MailMessage(myAddress, myAddress);
message.Body = "Hello";
message.Attachments.Add(new Attachment(#"Test.txt"));
var client = new YahooMailClient();
client.Send(message);
}
}
public class YahooMailClient : SmtpClient
{
public YahooMailClient()
: base("smtp.mail.yahoo.com", 25)
{
Credentials = new YahooCredentials();
}
}
public class YahooCredentials : ICredentialsByHost
{
public NetworkCredential GetCredential(string host, int port, string authenticationType)
{
return new NetworkCredential("jhered#yahoo.com", "mypwd");
}
}
}

Categories