I recently deployed a C# script that utilizes System.Net.Mail functionality. The mailing system will work by sending a photobooth screenshot as an attachment to each respective email addresses once users filled up the form ( hence each users are supposed to receive an unique image attachments ).
The script works flawlessly but I am suffering from one issue: once the internet connection became slow or suffer from some random down time, the email addresses will overlap and current guests will receive attachments from previous users.
I am wondering if there are any method that can create/compose a new mail without interrupting the current sending processes. I am using Gmail btw.
Below is the code I'm using:
MailMessage mail = new MailMessage();
mail.Attachments.Clear();
mail.From = new MailAddress("#gmail.com");
mail.To.Add(email);
mail.Subject = "";
mail.Body = "";
mail.Attachments.Add(new Attachment("screenshot.jpg"));
//mail.Attachments.Add (new Attachment("screenshot.jpg"));
SmtpClient smtpServer = new SmtpClient("smtp.gmail.com");
smtpServer.Port = 587;
smtpServer.Credentials = new System.Net.NetworkCredential("#gmail.com", "") as ICredentialsByHost;
smtpServer.EnableSsl = true;
ServicePointManager.ServerCertificateValidationCallback =
delegate(object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
return true;
};
smtpServer.Send(mail);
//Debug.Log("success");
sent = true;
Best solution would be to decouple the processes of creating and sending the emails.
Create each mail message and drop it in a queue. You can then have the mail sending process monitoring the queue and delivering any messages there as quickly as it can.
Send the mails on a separate thread, for example using tasks.
Task.Factory.StartNew(() => smtpServer.Send(mail));
This is a web application, correct? And is this line of code pulling from a physical file on the disk?:
mail.Attachments.Add (new Attachment ("screenshot.jpg"));
If so then there's your problem. Web applications are multi-threaded. So lots of users could be modifying that file at the same time. In the time it takes the application to go from the step of modifying that file to the step of reading that file, another user may have modified it.
You have a couple of options:
Create a unique file name for each time a user goes through this process. Do you need to keep the files afterward? I'm guessing not, since you overwrite them. So use something like Path.GetTempFileName() to create a temporary file and pass that from the saving step to the reading/emailing step instead of hard-coding the same file name.
Never save the file to disk, and instead just pass the byte[] of the file's data from the uploading step to the sending step, building the Attachment from the byte array.
Related
I'm working with C# on Windows servers for a web application stored on the IIS Server.
I would like to create an eml file from :
an html content (string)
some attachments that are loaded in memory
a string subject
string recipients
string sender
The main problem is that I am not allowed to store files on the host server (not even in a temporary directory or if I delete them after).
I saw many threads explaining how to create an eml file with the help of SmtpClient. But we always need to use a directory to save the file.
Do someone knows a way to do that ? Or to create a directory in memory (which seems undoable) ?
Thanks for everyone who will read me
[EDIT]
Using jstedfast's answer below and the Mime documentation, I could figure a way. Here is a POC in case someone needs it later.
var message = new MimeMessage();
message.From.Add(new MailboxAddress("Joey", "joey#friends.com"));
message.To.Add(new MailboxAddress("Alice", "alice#wonderland.com"));
message.Subject = "How you doin?";
var builder = new BodyBuilder();
// Set the plain-text version of the message text
builder.TextBody = #"Hey Alice,
What are you up to this weekend? Monica is throwing one of her parties on
Saturday and I was hoping you could make it.
Will you be my +1?
-- Joey
";
// We may also want to attach a calendar event for Monica's party...
builder.Attachments.Add("test.pdf", attachmentByteArray);
// Now we just need to set the message body and we're done
message.Body = builder.ToMessageBody();
using (var memory = new MemoryStream())
{
message.WriteTo(memory);
}
Look into using MimeKit.
You can write the MimeMessage objects to any type of stream that you want, including a MemoryStream.
This question already has answers here:
SMTP Send is locking up my files - c#
(5 answers)
Closed 4 years ago.
Why, after this code executes, can I not manually delete the generated file? I get this error message:
The action can't be completed because the file is open in IIS Express Workers Process.
The code:
System.IO.File.AppendAllText("Filename.txt", "Some Text");
The application is a web application and running in IIS. My understanding was AppendAllText automatically closes the file handle.
I am not using this code to log. The purpose of the code is to create a CSV file that is then attached to an email. If I execute the code and email the file, the file is never sent. If I then restart IIS (to release the lock) and execute only the code to mail the file (no generation), it works.
The code to attach the file...
SmtpClient mailClient = new SmtpClient();
MailMessage msg = new MailMessage();
... Add From, Subject, Body, etc... here
msg.Attachments.Add(new Attachment(filename));
mailClient.Send(msg);
No exceptions are thrown, nothing is written to the log file indicating problems. The email is simply not sent and if I go to the generated file and attempt to delete it, I cannot as IIS has it locked. I believe the lock is why the email is never sent.
File.AppendAllText close the file itself after writing it. But you are facing the issue probably because the file is shared across multiple session in your web application. Use lock to prevent concurrent access.
You should handle the code you provided in a different way for web application.
using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
System.IO.StreamWriter writer = new System.IO.StreamWriter(ms);
writer.Write("Some Text");
writer.Flush();
writer.Dispose();
ms.Position = 0;
System.Net.Mime.ContentType ct = new System.Net.Mime.ContentType(System.Net.Mime.MediaTypeNames.Text.Plain);
System.Net.Mail.Attachment attach = new System.Net.Mail.Attachment(ms, ct);
attach.ContentDisposition.FileName = "Filename.txt";
SmtpClient mailClient = new SmtpClient();
MailMessage msg = new MailMessage();
msg.Attachments.Add(attach);
mailClient.Send(msg);
}
In the above example attachment is created in-memory insteed of storing in file-system.
I'm using WinForms. I made a simple Image Viewer application using a picturebox to display my images. I made a way to create temporary files. These files are always picture files. When my application is done using the image i want to be able to delete these temporary on FormClosing files located at: C:\Users\taji01\AppData\Local\Temp\8bd93a0dec76473bb82a12488fd350af To do that i cannot simply call File.Delete(C://picture.jpg) because my application is still using them even though there is another picture displaying in my application. So i tried to dispose it but i couldn't figure how how to do that. Should i be using a using statement? Is there a better way to dispose and delete the file or is there a way to make this work?
_fileName = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
File.Copy(imagePath, _fileName);
_stream = new FileStream(_fileName, FileMode.Open, FileAccess.Read, FileOptions.DeleteOnClose);
this._Source = Image.FromStream(_stream);
Error: "The process cannot access the file C:\picture.jpg because it is being used by another process" Exeption thrown: 'System.IO.IO.Exception' in msconrlib.dll (The process cannot access the file 'C:\picture.jpg' because it is being used by another procesas")
You need to Close() your FileStream.
I think a Transaction manager will do what you want. Check out .NET Transactional File Manager. When you rollback your Transaction, it should delete your temp files automatically, as long as they were created within the Transaction scope.
Here you need to dispose the object of MailMessage.
For Ex.
// Sends email using SMTP with default network credentials
public static void SendEmailToCustomer(string To, string From, string BCC, string Subject, string Body, bool IsBodyHtml, string attachedPath = "") {
//create mail message
MailMessage message = !string.IsNullOrEmpty(From) ? new MailMessage(From, To) : new MailMessage(From, To);
//create mail client and send email
SmtpClient emailClient = new SmtpClient();
//here write your smtp details below before sending the mail.
emailClient.Send(message);
//Here you can dispose it after sending the mail
message.Dispose();
//Delete specific file after sending mail to customer
if (!string.IsNullOrEmpty(attachedPath))
DeleteAttachedFile(attachedPath);
}
//Method to delete attached file from specific path.
private static void DeleteAttachedFile(string attachedPath) {
File.SetAttributes(attachedPath, FileAttributes.Normal);
File.Delete(attachedPath);
}
This is working fine locally but when I sent it to another person in the company (same exchange server) using Outlook on a mac, it does not work correctly. Instead, the image is replaced with the text ATT00001 and the image becomes an attachment called ATT0001
It was tricky to get this working in the first place, here is the code I use:
var assembly = Assembly.GetExecutingAssembly();
var stream = assembly.GetManifestResourceStream("EmailManager.Kitten.jpg");
var inlineLogo = new LinkedResource(stream, "image/jpg");
inlineLogo.ContentId = "CompanyLogo";
body = body.Replace("{logourl}", string.Format("cid:{0}", inlineLogo.ContentId));
var view = AlternateView.CreateAlternateViewFromString(body, null, MediaTypeNames.Text.Html);
view.LinkedResources.Add(inlineLogo);
mailMessage.Body = body;
mailMessage.Subject = "Check out the kitty logo";
mailMessage.AlternateViews.Add(view);
mailMessage.IsBodyHtml = true;
SmtpClient smtp = new SmtpClient(....);
smtp.Send(mailMessage);
The body is just a string of stuff with an <img src='{logourl}' /> in it.
Any suggestions of what I might do to fix or debug this? Alternatively is there a way to link to an external image without outlook blocking it by default (eg. having it come from the same server or similar).
Edit: I've read something about macs wanting the attachments listing at the end of the e-mail, could this be it? Although there is no way I can see from the above how to specify this behaviour. Also I am not entirely sure it's the problem.
can be one of the possible solution
i have found answer .bin file comes when _EmailLogo1 and
_EmailLogo are empty so need to check if it's empty/NULL or not !! if not empty/NULL then it should be linked otherwise don't !!
dynamic htmlView = AlternateView.CreateAlternateViewFromString(_Body.ToString(), null, "text/html");
if (!string.IsNullOrEmpty(_EmailLogo1)) {
LinkedResource logo = new LinkedResource(_EmailLogo);
logo.ContentId = "logo2";
htmlView.LinkedResources.Add(logo);
}
if (!string.IsNullOrEmpty(_EmailLogo))
{
LinkedResource logo1 = new LinkedResource(_EmailLogo1);
logo1.ContentId = "logo1";
htmlView.LinkedResources.Add(logo1);
aMessage.AlternateViews.Add(htmlView);
}
Some email clients are smart enough to display correctly whereas others require you to be very specific with your AlternateView and LinkedResource. Have you tried with other email clients e.g. Windows Live Mail?
Try specifying the ContentType of your AlternateView:
view.ContentType = new ContentType("text/html");
I had the same issue where Outlook did the same, displayed the image as an unnamed attachment however Windows Live Mail worked perfectly.
Fast forward to when we moved our SMTP server to Mandrill and we hit the same issue for all email clients where emails displayed as unnamed attachments and it turned out we didn't specify one of our ContentType's and it defaulted to "application/octet-stream"
I know that this question is a bit old, but I have noticed that some issues occur when you use ' instead of " wrapping the source.
Try instead to write it like this <img src="{logourl}" />
Hope this helps someone.
I have a requirement to send emails containing both text and Images.
So, I have .mhtml file that contains the content that needs to be emailed over.
I was using Chilkat for this, but in outlook 2007 it is showing the mhtml file as different attachments(html+images).
Can anyone suggest me some other component for sending mhtml emails.
FYI, I am using .Net 3.5
Also, I do not want to save the images on server before sending them.
Thank you!
I use plain old native MailMessage class. This previous answer can point you in right direction
EDIT: I built a similiar code some time ago, which captures an external HTML page, parse it's content, grab all external content (css, images, etc) and to send that through email, without saving anything on disk.
Here is an example using an image as an embedded resource.
MailMessage message = new MailMessage();
message.From = new MailAddress(fromEmailAddress);
message.To.Add(toEmailAddress);
message.Subject = "Test Email";
message.Body = "body text\nblah\nblah";
string html = "<body><h1>html email</h1><img src=\"cid:Pic1\" /><hr />" + message.Body.Replace(Environment.NewLine, "<br />") + "</body>";
AlternateView alternate = AlternateView.CreateAlternateViewFromString(html, null, MediaTypeNames.Text.Html);
message.AlternateViews.Add(alternate);
Assembly assembly = Assembly.GetExecutingAssembly();
using (Stream stream = assembly.GetManifestResourceStream("SendEmailWithEmbeddedImage.myimage.gif")) {
LinkedResource picture = new LinkedResource(stream, MediaTypeNames.Image.Gif);
picture.ContentId = "pic1"; // a unique ID
alternate.LinkedResources.Add(picture);
SmtpClient s = new SmtpClient();
s.Host = emailHost;
s.Port = emailPort;
s.Credentials = new NetworkCredential(emailUser, emailPassword);
s.UseDefaultCredentials = false;
s.Send(message);
}
}
System.Net would be the one that you are looking for.<br/>
MailMessage is used to compose new mail.<br/>
SMTPClient is used to send mail.
NetworkCredentials would be used to attach username and password for making request to sending mail.
Coming to your question how to add images.
You need to set isHtml=true for MailMessage
Since you want to send mail relative paths in the html won't work like ../directory/imagename.formate
in such case you need to give completed path to the image location that's websiteUrl/directory/imagename.formate
To get complete Url dynamically you can use like this Request.Uri.GetLeftParth(URIPartial.Authority)+VitrtualToAbsolute.getAbsolute("~")
I'm not sure about last line since I have wrote directly over here. You just need to use it and have good luck ;-)
You need to explicitly set the MIME type to multipart/related. Change the MailMessage.Body to include the content of the MHTML file in it. Finally add a new item to the MailMessage.AlternateViews collection to define the correct MIME type. The following link from MSDN has a very good example how to set it up:
MailMessage.AlternateViews Property