Break apart email attachment if zip is too big to email - c#

So I can send up to 25MB in an email attachment. I am looking for away to check how big the file size is and if its too big break it apart and send in two emails. (Or if there is a better way to do it)?
Here is where I am sending the email with the attachment:
public static void SendEmail(List<string> recipients, MemoryStream output, string from, string subject, string htmlMessage, bool isHtml = true)
{
var host = ConfigurationManager.AppSettings["emailHost"];
var emailFrom = ConfigurationManager.AppSettings["FromEmail"];
try
{
MailMessage mail = new MailMessage();
mail.From = new MailAddress(from);
foreach (var r in recipients)
{
mail.To.Add(r);
}
mail.Subject = subject;
mail.IsBodyHtml = isHtml;
mail.Body = htmlMessage;
//string result = System.Text.Encoding.UTF8.GetString(output.ToArray());
SmtpClient SmtpServer = new SmtpClient(host);
SmtpServer.Port = 25;
Attachment myZip = new Attachment(output, "ClientStatements.zip");
mail.Attachments.Add(myZip);
SmtpServer.Send(mail);
}
catch (Exception ex)
{
FMBUtilities.Logger.LogErrorToSql2012PrdAndEmailTeam("DBQueries", "SendEmail", ex);
}
}
Here is where i am creating the zip file to send:
if (emails.ToString() != "")
{
var allEmails = emails[0].Split(',');
foreach (var email in allEmails)
{
if (emailValid.IsMatch(email))
{
everyEmail.Add(email);
}
else
{
return Json(new { success = false, message = $"* Not valid email address: {email}.\n\n * Please double check and try again." });
}
}
MemoryStream output = new MemoryStream();
List<string> distinctFiles = allPaths
.GroupBy(x => x.Split(new char[] { '\\' }).Last())
.Select(x => x.First())
.ToList();
using (ZipFile zip = new ZipFile())
{
zip.AddFiles(distinctFiles, #"\");
zip.Save(output);
output.Position = 0;
DBQueries.SendEmail(everyEmail, output, fromAddress, "Client Statement Reports", "Here are your requested Client Statements", true);
}
I have found some things about setting the file size in the web.config file and things of that sort but just dont know the best way to go about this.

Related

Sending email with attachment of zip folder not attaching the zip folder

I am trying to attach a zip file after downloading files from the server and then sending them in an email.
Here is where i am downloading the files:
else if (radio[0] == "Email Statements")
{
// Make array of emails into List for sending in email
if (emails.ToString() != "")
{
var allEmails = emails[0].Split(',');
foreach (var email in allEmails)
{
if (emailValid.IsMatch(email))
{
everyEmail.Add(email);
}
else
{
return Json(new { success = false, message = $"* Not valid email address: {email}.\n\n * Please double check and try again." });
}
List<string> distinctFiles = allPaths
.GroupBy(x => x.Split(new char[] { '\\' }).Last())
.Select(x => x.First())
.ToList();
using (ZipFile zip = new ZipFile())
{
zip.AddFiles(distinctFiles, #"\");
MemoryStream output = new MemoryStream();
zip.Save(output);
//return File(output.ToArray(), "application/zip");
DBQueries.SendEmail(everyEmail, output, fromAddress, "Client Statement Reports", "Here are your requested Client Statements", true);
}
Then the DBQueries.SendEmail function will send me here and this is where I am having trouble. How can I get my downloaded files to this function correctly?
public static void SendEmail(List<string> recipients, MemoryStream output, string from, string subject, string htmlMessage, bool isHtml = true)
{
var host = ConfigurationManager.AppSettings["emailHost"];
try
{
MailMessage mail = new MailMessage();
mail.From = new MailAddress(from);
foreach (var r in recipients)
{
mail.To.Add(r);
}
mail.Subject = subject;
mail.IsBodyHtml = isHtml;
mail.Body = htmlMessage;
//string result = System.Text.Encoding.UTF8.GetString(output.ToArray());
SmtpClient SmtpServer = new SmtpClient(host);
SmtpServer.Port = 25;
Attachment myZip = new Attachment(output);
mail.Attachments.Add(myZip);
SmtpServer.Send(mail);
}

"noname" attachment shown when email sent has image

HELP! So i have this issue wherein theres a "noname" attachment when sending emails with pictures as seen below
Now, i want this to remove since it is not part of my emailer.
heres is my code below
public static bool SendEmailNotification(List<string> toList, List<string> ccList, string subject, string body)
{
bool isSent = true;
try
{
var mail = new MailMessage();
var smtpClient = new SmtpClient(Constants.SMTP.SMTPClient);
var alternateView = AlternateView.CreateAlternateViewFromString(body, null, Constants.SMTP.Format);
mail.From = new MailAddress(Constants.SMTP.EmailAddress);
if (toList != null && toList.Any())
{
foreach (var email in toList)
{
mail.To.Add(email);
}
if (ccList != null && ccList.Any())
{
foreach (var email in ccList)
{
mail.CC.Add(email);
}
}
mail.Subject = subject;
mail.IsBodyHtml = true;
mail.AlternateViews.Add(alternateView);
smtpClient.Credentials = new NetworkCredential(Constants.SMTP.EmailAddress,
Constants.SMTP.EmailPassword, Constants.SMTP.Email);
smtpClient.Send(mail);
}
else
{
isSent = false;
}
}
catch
{
isSent = false;
}
return isSent;
}
The attachment is actually your image payload in binary. It's set to "noname". Reason being is certain parameters aren't set properly. In this case, to do it properly refer to https://brutaldev.com/post/sending-html-e-mail-with-embedded-images-(the-correct-way)
if( attachementFile!=null)
{
Attachment attachment = new Attachment(attachementFile, MediaTypeNames.Application.Octet);
ContentDisposition disposition = attachment.ContentDisposition;
disposition.CreationDate = File.GetCreationTime(attachementFile);
disposition.ModificationDate = File.GetLastWriteTime(attachementFile);
disposition.ReadDate = File.GetLastAccessTime(attachementFile);
disposition.FileName = Path.GetFileName(attachementFile);
disposition.Size = new FileInfo(attachementFile).Length;
disposition.DispositionType = DispositionTypeNames.Attachment;
return attachment;
}
Here's the code from the link in #kendolew's answer with an example on how to properly include an attachment:
public static void SendMessageWithEmbeddedImages()
{
string htmlMessage = #"<html>
<body>
<img src='cid:EmbeddedContent_1' />
</body>
</html>";
SmtpClient client = new SmtpClient("mail.server.com");
MailMessage msg = new MailMessage("noreply#deventerprise.net",
"someone#deventerprise.net");
// Create the HTML view
AlternateView htmlView = AlternateView.CreateAlternateViewFromString(
htmlMessage,
Encoding.UTF8,
MediaTypeNames.Text.Html);
// Create a plain text message for client that don't support HTML
AlternateView plainView = AlternateView.CreateAlternateViewFromString(
Regex.Replace(htmlMessage,
"<[^>]+?>",
string.Empty),
Encoding.UTF8,
MediaTypeNames.Text.Plain);
string mediaType = MediaTypeNames.Image.Jpeg;
LinkedResource img = new LinkedResource(#"C:\Images\MyImage.jpg", mediaType);
// Make sure you set all these values!!!
img.ContentId = "EmbeddedContent_1";
img.ContentType.MediaType = mediaType;
img.TransferEncoding = TransferEncoding.Base64;
img.ContentType.Name = img.ContentId;
img.ContentLink = new Uri("cid:" + img.ContentId);
htmlView.LinkedResources.Add(img);
//////////////////////////////////////////////////////////////
msg.AlternateViews.Add(plainView);
msg.AlternateViews.Add(htmlView);
msg.IsBodyHtml = true;
msg.Subject = "Some subject";
client.Send(msg);
}

Email sending with attachment issue

I am trying to send an email message to different users (with an attachment). The email is being sent, but only one user receives the attachment (image file). Other recipients receive something like empty picture, or picture with name and zero bytes.
I don't really understand what is going wrong. Here is code that I used for sending emails:
public void SendWithFile(string recipientName, string body, string subject = null, HttpPostedFileBase emailFile = null)
{
using (var msg = new MailMessage())
{
msg.To.Add(recipientName);
msg.From = new MailAddress(ConfigurationManager.AppSettings["MailServerSenderAdress"]);
msg.Subject = subject;
msg.Body = body;
msg.SubjectEncoding = Encoding.UTF8;
msg.BodyEncoding = Encoding.UTF8;
msg.IsBodyHtml = true;
msg.Priority = MailPriority.Normal;
using (Attachment data = new Attachment(emailFile.InputStream, Path.GetFileName(emailFile.FileName), emailFile.ContentType))
{
msg.Attachments.Add(data);
using (var client = new SmtpClient())
{
//client configs
try
{
client.Send(msg);
}
catch (Exception ex)
{
throw ex;
}
}
}
}
}
and here is where I call the send email method:
foreach (var recipent in notesRecipients)
{
if (!string.IsNullOrEmpty(userEmail))
{
if (emailFile != null)
emailService.SendWithFile(userEmail, message, null, emailFile);
}
}
You have to seek the stream from the beginning like below before you send the attachment:
emailFile.InputStream.Position = 0;
For more info you can refer to this question here: link

Mail sends but never returns

I've been working on a website where you can directly send an inquire mail to a specific email.
At first I thought that the mail was not sending because the site is just keep on loading after I click the send button. But when I check my email, I was surprise that the mail that I send was there. But when I checked my website(where I have the inquire form) it still loading.
The ViewBag that suppose to says "Your message has been sent!" still doesn't show, even though I already receive the mail. Seems like the
await smtp.SendMailAsync(message);
don't return. I'm new with this kind of thing. I hope someone could help me. Thank you in advance. Here is my controller:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Index(EmailFormModel model, IEnumerable<HttpPostedFileBase> files)
{
try
{
if (ModelState.IsValid)
{
List<string> paths = new List<string>();
foreach (var file in files)
{
if (file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(System.Web.HttpContext.Current.Server.MapPath("~/App_Data/uploads"), fileName);
file.SaveAs(path);
paths.Add(path);
}
}
var message = new MailMessage();
foreach (var path in paths)
{
var fileInfo = new FileInfo(path);
var memoryStream = new MemoryStream();
using (var stream = fileInfo.OpenRead())
{
stream.CopyTo(memoryStream);
}
memoryStream.Position = 0;
string fileName = fileInfo.Name;
message.Attachments.Add(new Attachment(memoryStream, fileName));
}
//Rest of business logic here
string EncodedResponse = Request.Form["g-Recaptcha-Response"];
bool IsCaptchaValid = (ReCaptcha.Validate(EncodedResponse) == "True" ? true : false);
if (IsCaptchaValid)
{
var body = "<p><b>Email From:</b> {0} ({1})</p><p><b>Subject:</b> {2} </p><p><b>Software Description:</b></p><p>{4}</p><p><b>Message:</b></p><p>{3}</p>";
message.To.Add(new MailAddress("email#mydomain.com")); // replace with valid value
message.From = new MailAddress("email#randomdomain.com"); // replace with valid value
message.Subject = "(Inquire for SELLING)";
message.Body = string.Format(body, model.FromName, model.FromEmail, model.FromSubject, model.Message, model.Desc);
message.IsBodyHtml = true;
using (var smtp = new SmtpClient())
{
var credential = new NetworkCredential
{
UserName = "email#mydomain.com", // replace with valid value
Password = "0000" // replace with valid value
};
smtp.Credentials = credential;
smtp.Host = "relay-hosting.secureserver.net";
smtp.Port = 25;
smtp.Timeout = 1000;
smtp.DeliveryMethod = SmtpDeliveryMethod.Network;
smtp.UseDefaultCredentials = false;
smtp.SendCompleted += (s, e) =>
{
//delete attached files
foreach (var path in paths)
System.IO.File.Delete(path);
};
await smtp.SendMailAsync(message);
ViewBag.Message = "Your message has been sent!";
ModelState.Clear();
return View("Index");
}
}
else
{
TempData["recaptcha"] = "Please verify that you are not a robot!";
}
} return View(model);
}
catch (Exception ex)
{
return View("Error");
}
}
I have ran into this same issue before. The Attachments are being held onto and need to be disposed of before you can delete them. Without the call to .Dispose the files are locked down. Try this instead:
[
HttpPost,
ValidateAntiForgeryToken
]
public async Task<ActionResult> Index(EmailFormModel model,
IEnumerable<HttpPostedFileBase> files)
{
try
{
if (ModelState.IsValid)
{
string EncodedResponse = Request.Form["g-Recaptcha-Response"];
bool IsCaptchaValid = ReCaptcha.Validate(EncodedResponse) == "True";
if (IsCaptchaValid)
{
var paths = GetUploadPaths(files);
using (var message = ConstructMailMessage(model))
{
AppendAttachments(paths, message.Attachments);
using (var smtp = new SmtpClient())
{
var credential = new NetworkCredential
{
UserName = "...", // replace with valid value
Password = "..." // replace with valid value
};
smtp.Credentials = credential;
smtp.Host = "relay-hosting.secureserver.net";
smtp.Port = 25;
smtp.Timeout = 1000;
smtp.DeliveryMethod = SmtpDeliveryMethod.Network;
smtp.UseDefaultCredentials = false;
smtp.SendCompleted += (s, e) =>
{
// Ensure disposed first.
foreach (var a in message.Attachments) a.Dispose();
foreach (var path in paths) File.Delete(path);
};
await smtp.SendMailAsync(message);
ViewBag.Message = "Your message has been sent!";
ModelState.Clear();
return View("Index");
}
}
}
else
{
TempData["recaptcha"] = "Please verify that you are not a robot!";
}
}
return View(model);
}
catch (Exception ex)
{
return View("Error");
}
}
I tried a slightly different approach with regard to separating some of the core logic. For example, there is now a helper method for getting the upload file paths GetUploadPaths and one for appending attachments the the .Attachments instance via the AppendAttachments. Additionally, there is now a ConstructMailMessage function that does that work as well.
public List<string> GetUploadPaths(IEnumerable<HttpPostedFileBase> files)
{
var paths = new List<string>();
foreach (var file in files)
{
if (file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(HttpContext.Current.Server.MapPath("~/App_Data/uploads"), fileName);
file.SaveAs(path);
paths.Add(path);
}
}
return paths;
}
public MailMessage ConstructMailMessage(EmailFormModel model)
{
var message = new MailMessage();
var body = "<p><b>Email From:</b> {0} ({1})</p><p><b>Subject:</b> {2} </p><p><b>Software Description:</b></p><p>{4}</p><p><b>Message:</b></p><p>{3}</p>";
message.To.Add(new MailAddress("email#mydomain.com")); // replace with valid value
message.From = new MailAddress("email#randomdomain.com"); // replace with valid value
message.Subject = "(Inquire for SELLING)";
message.Body = string.Format(body, model.FromName, model.FromEmail, model.FromSubject, model.Message, model.Desc);
message.IsBodyHtml = true;
return message;
}
public void AppendAttachments(List<string> paths, AttachmentCollection attachments)
{
foreach (var path in paths)
{
var fileInfo = new FileInfo(path);
var memoryStream = new MemoryStream();
using (var stream = fileInfo.OpenRead())
{
stream.CopyTo(memoryStream);
}
memoryStream.Position = 0;
string fileName = fileInfo.Name;
attachments.Add(new Attachment(memoryStream, fileName));
}
}

C# Sending Email with attached file (image)

My method sends an email using a SMTP Relay Server.
Everything works fine (the email gets sent), except for that the attached file (the image) is somehow compressed/notexistent and not able to retrieve from the email.
The method looks like this:
public static bool SendEmail(HttpPostedFileBase uploadedImage)
{
try
{
var message = new MailMessage() //To/From address
{
Subject = "This is subject."
Body = "This is text."
};
if (uploadedImage != null && uploadedImage.ContentLength > 0)
{
System.Net.Mail.Attachment attachment;
attachment = new System.Net.Mail.Attachment(uploadedImage.InputStream, uploadedImage.FileName);
message.Attachments.Add(attachment);
}
message.IsBodyHtml = true;
var smtpClient = new SmtpClient();
//SMTP Credentials
smtpClient.Send(message);
return true;
}
catch (Exception ex)
{
//Logg exception
return false;
}
}
The uploadedImage is not null.
ContentLength is 1038946 bytes (correct size).
However, the email that is being sent contains the image as an attachment with correct filename, although it's size is 0 bytes.
What am I missing?
The second parameter of constructor of System.Net.Mail.Attachment is not the file name. It's the content type.
And perhaps ensure your stream position is 0 before to create attachment
#ChrisRun,
You should change the parameter HttpPostedFileBase as byte[] for example. This way you could re-use your class in more places.
Try changing FileName for ContentType and add the MediaTypeNames.Image.Jpeg.
Also, add the using directive for dispose the MailMessage and SmtpClient
using (var message = new MailMessage
{
From = new MailAddress("from#gmail.com"),
Subject = "This is subject.",
Body = "This is text.",
IsBodyHtml = true,
To = { "to#someDomain.com" }
})
{
if (imageFile != null && imageFile.ContentLength > 0)
{
message.Attachments.Add(new Attachment(imageFile.InputStream, imageFile.ContentType, MediaTypeNames.Image.Jpeg));
}
using (var client = new SmtpClient("smtp.gmail.com")
{
Credentials = new System.Net.NetworkCredential("user", "password"),
EnableSsl = true
})
{
client.Send(message);
}
}
Cheers

Categories

Resources