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));
}
}
Related
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);
}
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.
I am using LinkedResource for send inline images. İf i send one image, its work fine but if send two or more inline images SMTP sending as an inline images also as an Attachment.
İf I remove adding attachment part, images will be unreachable in the inbox.
How to hide inline image from Attachment any idea?
Here my code
public void SendEmailWithImageTo(string subject, string toMail, string decrypt, List<string> imageNames, Template template, List<UploadFile> UploadFiles = null)
{
this.FromMail = new MailAddress(this.MailUser, this.DisplayName);
List<MailImageModel> mailImageModels = new List<MailImageModel>();
AlternateView avHtml;
LinkedResource inline;
Attachment att;
string mailBodyForInfoFormat = "";
string tempTemplate = template.HtmlContent;
tempTemplate = string.Format(tempTemplate, decrypt);
foreach (var item in imageNames)
{
string path = $#"{HttpRuntime.AppDomainAppPath}\Image\MailingImage\{item}";
avHtml = AlternateView.CreateAlternateViewFromString(tempTemplate, null, MediaTypeNames.Text.Html);
if (item.Contains("png"))
{
inline = new LinkedResource(path);
inline.ContentType =new ContentType("image/png");
}
else if (item.Contains("gif"))
{
inline = new LinkedResource(path, MediaTypeNames.Image.Gif);
}
else
{
inline = new LinkedResource(path, MediaTypeNames.Image.Jpeg);
}
inline.ContentId = "MyPic";
avHtml.LinkedResources.Add(inline);
att = new Attachment(path);
att.ContentDisposition.Inline = true;
tempTemplate = tempTemplate.Replace("{", "{{").Replace("}", "}}");
mailBodyForInfoFormat = string.Format(tempTemplate, att.ContentId);
MailImageModel mailImageModel = new MailImageModel
{
AttachPath = path,
AlternateView = avHtml
};
mailImageModels.Add(mailImageModel);
}
mailBodyForInfoFormat = mailImageModels.Count > 0 ? mailBodyForInfoFormat : tempTemplate;
using (MailMessage mm = new MailMessage())
{
mm.From = this.FromMail;
mm.Subject = subject;
mm.Body = mailBodyForInfoFormat;
mm.IsBodyHtml = true;
mm.To.Clear();
mm.Bcc.Clear();
mm.CC.Clear();
mm.To.Add(toMail);
SmtpClient smtp = new SmtpClient();
smtp.EnableSsl = UseSSL == "1" ? true : false;
if (RelayActive == "0")
{
smtp.Host = MailServer;
NetworkCredential NetworkCred = new NetworkCredential(this.MailUser, this.MailPassword);
smtp.UseDefaultCredentials = true;
smtp.Credentials = NetworkCred;
smtp.Port = Convert.ToInt32(this.MailPort);
}
else
{
smtp.Host = RelayMailServer;
smtp.Port = Convert.ToInt32(this.RelayPort);
}
if (mailImageModels.Count > 0)
{
if (mailImageModels.Count != mm.Attachments.Count)
{
foreach (var item in mailImageModels)
{
mm.Attachments.Add(new Attachment(item.AttachPath));
mm.AlternateViews.Add(item.AlternateView);
}
}
}
if (UploadFiles.Count > 0)
{
foreach (var item in UploadFiles)
{
System.Net.Mail.Attachment attachment = new System.Net.Mail.Attachment(item.Path);
attachment.Name = item.FileName;
mm.Attachments.Add(attachment);
//mm.Attachments.Add(new Attachment(item.Path));
}
}
smtp.Send(mm);
smtp.Dispose();
}
}
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);
}
I am trying to get an uploaded file to be sent as an attachment in my ashx file. Here is the code I am using:
HttpPostedFile fileupload = context.Request.Files[0];
//filename w/o the path
string file = Path.GetFileName(fileupload.FileName);
MailMessage message = new MailMessage();
//*****useless stuff********
message.To.Add("abc#xxx.com");
message.Subject = "test";
message.From = new MailAddress("test#aaa.com");
message.IsBodyHtml = true;
message.Body = "testing";
//*****useless stuff********
//Fault line
message.Attachments.Add(new Attachment(file, MediaTypeNames.Application.Octet))
//Send mail
SmtpClient smtp = new System.Net.Mail.SmtpClient("xxxx", 25);
smtp.UseDefaultCredentials = false;
smtp.Credentials = new NetworkCredential("xxx", "xxxx");
smtp.Send(message);
I am able to send the email without the attachment.
Do I need to save the file first and then add to attachment?
You do NOT need to, nor should you, save attachments to the server unnecessarily. ASP Snippets has an article on how to do it in ASP.NET WebForms.
Doing it in C# MVC is even nicer:
public IEnumerable<HttpPostedFileBase> UploadedFiles { get; set; }
var mailMessage = new MailMessage();
// ... To, Subject, Body, etc
foreach (var file in UploadedFiles)
{
if (file != null && file.ContentLength > 0)
{
try
{
string fileName = Path.GetFileName(file.FileName);
var attachment = new Attachment(file.InputStream, fileName);
mailMessage.Attachments.Add(attachment);
}
catch(Exception) { }
}
}
Following in the footsteps of Serj Sagan, here's a handler using webforms, but with <input type="file" name="upload_your_file" /> instead of the <asp:FileUpload> control:
HttpPostedFile file = Request.Files["upload_your_file"];
if (file != null && file.ContentLength > 0)
{
string fileName = Path.GetFileName(file.FileName);
var attachment = new Attachment(file.InputStream, fileName);
mailMessage.Attachments.Add(attachment);
}
This is useful if you don't need (or can't add) a runat="server" on your form tag.
You can do like this:
private void btnSend_Click(object sender,EventArgs e)
{
MailMessage myMail = new MailMessage();
myMail.To = this.txtTo.Text;
myMail.From = "<" + this.txtFromEmail.Text + ">" + this.txtFromName.Text;
myMail.Subject = this.txtSubject.Text;
myMail.BodyFormat = MailFormat.Html;
myMail.Body = this.txtDescription.Text.Replace("\n","<br>");
//*** Files 1 ***//
if(this.fiUpload1.HasFile)
{
this.fiUpload1.SaveAs(Server.MapPath("MyAttach/"+fiUpload1.FileName));
myMail.Attachments.Add(new MailAttachment(Server.MapPath("MyAttach/"+fiUpload1.FileName)));
}
//*** Files 2 ***//
if(this.fiUpload2.HasFile)
{
this.fiUpload2.SaveAs(Server.MapPath("MyAttach/"+fiUpload2.FileName));
myMail.Attachments.Add(new MailAttachment(Server.MapPath("MyAttach/"+fiUpload2.FileName)));
}
SmtpMail.Send(myMail);
myMail = null;
this.pnlForm.Visible = false;
this.lblText.Text = "Mail Sending.";
}
FileName is the name of the file on the client, not on the server. You will need to use SaveAs or the InputStream to get any content into the attachment.
Here is a link to the MSDN documentation.