Exception during delete file attached to a sent email - c#

After successfully sending email with attachment I have to delete files I've sent as attachement.
Files are in use so I have an exception.
I've used the code of the documentation. I'm using a method to create and send the email so everything is disposed automatically after calling it.
MimeMessage eMail = new MimeMessage();
eMail.From.Add (new MailboxAddress(fromDescription, fromAddress));
foreach (string to in toAddress)
eMail.To.Add(new MailboxAddress(to));
if (ccAddress != null)
foreach (string cc in ccAddress)
eMail.Cc.Add(new MailboxAddress(cc));
if (ccnAddress != null)
foreach (string ccn in ccnAddress)
eMail.Bcc.Add(new MailboxAddress(ccn));
eMail.Subject = subject;
var Body = new TextPart("plain")
{
Text = body
};
// now create the multipart/mixed container to hold the message text and the attachment
var multipart = new Multipart("mixed");
multipart.Add(Body);
if (attachments != null)
{
foreach (string attachmentPath in attachments)
{
// create an attachment for the file located at path
var attachment = new MimePart(MimeTypes.GetMimeType(attachmentPath))
{
Content = new MimeContent(File.OpenRead(attachmentPath), ContentEncoding.Default),
ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
ContentTransferEncoding = ContentEncoding.Base64,
FileName = Path.GetFileName(attachmentPath)
};
multipart.Add(attachment);
}
}
// now set the multipart/mixed as the message body
eMail.Body = multipart;
using (var client = new SmtpClient())
{
// For demo-purposes, accept all SSL certificates (in case the server supports STARTTLS)
//client.ServerCertificateValidationCallback = (s, c, h, e) => true;
client.Connect(SmtpHost, SmtpPort, SecureSocketOptions.SslOnConnect);
// Note: since we don't have an OAuth2 token, disable
// the XOAUTH2 authentication mechanism.
//client.AuthenticationMechanisms.Remove("XOAUTH2");
// Note: only needed if the SMTP server requires authentication
client.Authenticate(SmtpUser, SmtpPassword);
client.Send(eMail);
client.Disconnect(true);
}
What's wrong? Anyoone could help me?
Thank you

You need to dispose the stream you opened for the attachment:
File.OpenRead(attachmentPath)
You could do something like this:
var streams = new List<Stream> ();
if (attachments != null) {
foreach (string attachmentPath in attachments) {
// create an attachment for the file located at path
var stream = File.OpenRead(attachmentPath);
var attachment = new MimePart(MimeTypes.GetMimeType(attachmentPath)) {
Content = new MimeContent(stream, ContentEncoding.Default),
ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
ContentTransferEncoding = ContentEncoding.Base64,
FileName = Path.GetFileName(attachmentPath)
};
multipart.Add(attachment);
streams.Add (stream);
}
}
And then, after sending the message, do this:
foreach (var stream in streams)
stream.Dispose ();

Related

What type of variable am I passing to the API controller while uploading the file?

Here is the complete code on how to send emails WITH ATTACHMENTS via API.
I had problem with deleting the file after sending it to smtp server. And I couldn't figure out how to pass additional data along with the files.
This article helped https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/sending-html-form-data-part-2
Thanks you for help.
var formData = new FormData();
formData.append("to", "g#gmail.com");
formData.append("subject", "Yo man");
formData.append("body", "I'm happy");
formData.append("file[]", $('#fileToAttach')[0].files[0]);
$.ajax({
url: "/api/emailwithattachment",
type: 'POST',
data: formData,
processData: false,
contentType: false,
})
[HttpPost]
[AllowAnonymous]
[Route("api/emailwithattachment")]
public async Task<string> UploadFile()
{
var ctx = HttpContext.Current;
var root = ctx.Server
.MapPath("~/UploadedFiles");
var provider =
new MultipartFormDataStreamProvider(root);
await Request.Content
.ReadAsMultipartAsync(provider);
var To = "";
var Subject = "";
var Body = "";
foreach (var key in provider.FormData.AllKeys)
{
foreach (var val in provider.FormData.GetValues(key))
{
if (key == "to")
To = val;
if (key == "subject")
Subject = val;
if (key == "body")
Body = val;
}
}
MailMessage mail = new MailMessage();
SmtpClient SmtpServer = new SmtpClient("smtp.gmail.com", 587);
mail.From = new MailAddress("g#gmail.com");
mail.To.Add(To);
mail.Subject = Subject;
mail.Body = Body;
SmtpServer.Credentials = new NetworkCredential("g#gmail.com", "password");
SmtpServer.EnableSsl = true;
System.Net.Mail.Attachment attachment;
List<string> filePaths = new List<string>();
foreach (var file in provider.FileData)
{
var fileName = "CONF " + " " + DateTime.Now.ToString("yyyy-MM-ddTHH-mm-ss") + ".pdf";
var localFileName = file.LocalFileName;
var filePath = Path.Combine(root, fileName);
File.Move(localFileName, filePath);
attachment = new System.Net.Mail.Attachment(filePath);
mail.Attachments.Add(attachment);
filePaths.Add(filePath);
}
SmtpServer.Send(mail);
mail.Dispose();/////Here was the problem
foreach (var file in filePaths)
{
try
{
Console.WriteLine(file);
File.Delete(file);
}
catch (Exception e)
{
return e.Message;
}
}
return "success";
}
You need to figure out wich other process is using the file. And then close the process, or at least get it to release the file. However the 90% case for this exception, is that you opened the file in this programm before and did not properly release it.
File and Networking classes involve unmanaged resources - OS Level handles. Their classes are marked as Disposeable. They need to be properly disposed off - after use - to properly release those unmanaged resources. My rule for Disposeable classes of all shapes and forms is: "Create. Use. Dispose. All in the same piece of code, ideally using a using statement."

How to Send a MimeMessage

May I know how can I send out a MimeMessage?
Below is my code snippet:
MimeMessage eml = MimeMessage.Load(savedEmlFullFilePath);
MimeMessage toSend = Reply(eml,true); //to send out this message
public static MimeMessage Reply(MimeMessage message, bool replyToAll)
{
var reply = new MimeMessage();
// reply to the sender of the message
if (message.ReplyTo.Count > 0)
{
reply.To.AddRange(message.ReplyTo);
}
else if (message.From.Count > 0)
{
reply.To.AddRange(message.From);
}
else if (message.Sender != null)
{
reply.To.Add(message.Sender);
}
if (replyToAll)
{
reply.To.AddRange(message.To);
reply.Cc.AddRange(message.Cc);
}
// set the reply subject
if (!message.Subject.StartsWith("Re:", StringComparison.OrdinalIgnoreCase))
reply.Subject = "Re:" + message.Subject;
else
reply.Subject = message.Subject;
// construct the In-Reply-To and References headers
if (!string.IsNullOrEmpty(message.MessageId))
{
reply.InReplyTo = message.MessageId;
foreach (var id in message.References)
reply.References.Add(id);
reply.References.Add(message.MessageId);
}
// quote the original message text
using (var quoted = new StringWriter())
{
var sender = message.Sender ?? message.From.Mailboxes.FirstOrDefault();
quoted.WriteLine("On {0}, {1} wrote:", message.Date.ToString("f"), !string.IsNullOrEmpty(sender.Name) ? sender.Name : sender.Address);
using (var reader = new StringReader(message.TextBody))
{
string line;
while ((line = reader.ReadLine()) != null)
{
quoted.Write("> ");
quoted.WriteLine(line);
}
}
reply.Body = new TextPart("plain")
{
Text = quoted.ToString()
};
}
return reply;
}
Edit: Would also like to know if it is possible to parse in a MimeMessage into an EmailMessage to I can send it as a ResponseMessage using EWS...
From the MailKit README:
using (var client = new SmtpClient ()) {
// For demo-purposes, accept all SSL certificates (in case the server supports STARTTLS)
client.ServerCertificateValidationCallback = (s,c,h,e) => true;
client.Connect ("smtp.server.com", 587, false);
// Note: only needed if the SMTP server requires authentication
client.Authenticate ("username", "password");
client.Send (message);
client.Disconnect (true);
}

How to download attachment from email sent to google group mail address

Let's say my email a#company.com is in the google group which has email address group#company.com. We have a g suite service account which has enabled Google Apps Domain-wide Delegation.
We have emails send from b#companyb.com to our group email group#company.com, have subject Report from company b and attach the report in the email.
The issue is that the gmail api is able to list all messages but not able to list the attachments in each email.
Is there any way to do it?
Here's my code:
// here when I create the client, I use my email address `a#company.com`
using(var client = CreateClient())
{
UsersResource.MessagesResource.ListRequest request = client.Users.Messages.List("me");
request.Q = "from:b#compayb.com AND subject:Report from company b AND has:attachment"
// List messages.
var messageIds = request.Execute().Messages?.Select(m => m.Id) ?? new List<string>();
foreach(var mId in messageIds)
{
// https://developers.google.com/gmail/api/v1/reference/users/messages/attachments/get
Message message = client.Users.Messages.Get("me", messageId).Execute();
IList<MessagePart> parts = message.Payload.Parts;
foreach (MessagePart part in parts)
{
if (!String.IsNullOrEmpty(part.Filename))
{
String attId = part.Body.AttachmentId;
MessagePartBody attachPart = client.Users.Messages.Attachments.Get("me", messageId, attId).Execute();
// Converting from RFC 4648 base64 to base64url encoding
// see http://en.wikipedia.org/wiki/Base64#Implementations_and_history
String attachData = attachPart.Data.Replace('-', '+');
attachData = attachData.Replace('_', '/');
byte[] data = Convert.FromBase64String(attachData);
var file = new FileInfo(part.Filename);
File.WriteAllBytes(file.FullName, data);
}
}
}
}
If I forward the mail manually to the same address (so the receiver will be me), the code downloads the attachment.
I'd appreciate if you can help.
I've found the attachments are in the child MessagePart. So I wrote the recursive method to loop through all Parts to get all attachments.
// List<FileInfo> Files = new List<FileInfo>();
// client is created outside this method
private void GetAttachmentsFromParts(IList<MessagePart> parts, string messageId)
{
if (parts == null) return;
foreach (MessagePart part in parts)
{
if (!String.IsNullOrEmpty(part.Filename))
{
String attId = part.Body?.AttachmentId ?? null;
if(String.IsNullOrWhiteSpace(attId)) continue;
MessagePartBody attachPart = GmailServiceClient.Users.Messages.Attachments.Get("me", messageId, attId).Execute();
// Converting from RFC 4648 base64 to base64url encoding
// see http://en.wikipedia.org/wiki/Base64#Implementations_and_history
String attachData = attachPart.Data.Replace('-', '+');
attachData = attachData.Replace('_', '/');
byte[] data = Convert.FromBase64String(attachData);
var file = new FileInfo(part.Filename);
Files.Add(file);
File.WriteAllBytes(file.FullName, data);
}
if((part.Parts?.Count ?? 0) > 0)
GetAttachmentsFromParts(part.Parts, messageId);
}
}
All attachments will be stored in the List<FileInfo> Files

I am adding attachments to my mail but it is sending empty attachments

I am adding attachments to my mail but it is sending empty attachments.
I am looking for a quick solution please answer this.
I am adding attachments to my mail but it is sending empty attachments.
I am looking for a quick solution please answer this.
my API code->
var UserId = User.Identity.GetUserId();
UserId = UserId.ToString();
if (!string.IsNullOrEmpty(UserId))
{
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
var provider = await Request.Content.ReadAsMultipartAsync<MemoryMultipartFormDataStreamProvider>(new MemoryMultipartFormDataStreamProvider());
//access form data
NameValueCollection formData = provider.FormData;
//access files
IList<HttpContent> files = provider.Files;
List<string> fileNames = new List<string>();
List<string> filePaths = new List<string>();
List<Stream> inputs = new List<Stream>();
//HttpContent file1 = files[0];
foreach (HttpContent file1 in files)
{
var thisFileName = file1.Headers.ContentDisposition.FileName.Trim('\"');
fileNames.Add(thisFileName);
string filepath = String.Empty;
Stream input = await file1.ReadAsStreamAsync();
inputs.Add(input);
string directoryName = String.Empty;
string URL = String.Empty;
string tempDocUrl = WebConfigurationManager.AppSettings["DocsUrl"];
if (formData["ClientDocs"] != "ClientDocs")
{
var path = HttpRuntime.AppDomainAppPath;
directoryName = HttpContext.Current.Server.MapPath("~/Documents/");
filepath = System.IO.Path.Combine(directoryName, thisFileName);
filePaths.Add(filepath);
//Deletion exists file
if (File.Exists(filepath))
{
File.Delete(filepath);
}
}
using (var fileStream = new FileStream(filepath, FileMode.Create))
{
await file1.CopyToAsync(fileStream);
}
}
ContactModel contactModel = new ContactModel();
contactModel.FileName = fileNames;
contactModel.FilePath = filePaths;
contactModel.ToEmail = formData["To"];
contactModel.Message = formData["Message"];
contactModel.Subject = formData["Subject"];
contactModel.ContactId = Convert.ToInt64(formData["ContactId"]);
contactModel.ContentStream = inputs;
contactModel.ContactTypeId = 2;//Check Enums->ContactTypeId for more description
bool status = await _ContactService.ContactByEmail(contactModel);
HttpResponses.CreateResponsesMessage(HttpStatusCodeEnum.Ok.ToString("D"), Resource.EmailSent, responseMessage);
}
else
{
responseMessage = HttpResponses.CreateResponsesMessage(HttpStatusCodeEnum.UnAuthorized.ToString("D"), Resource.AuthorizationFail, responseMessage);
}
return xxx.CommonClass.HttpResponses.GetHttpResponseMessage(HttpStatusCode.OK, responseMessage);
}
my mailer code->
bool status;
if (contentStream != null && fileName != null)
{
var i = 0;
foreach (var contentStrea in contentStream)
{
System.Net.Mail.Attachment attachment;
attachment = new System.Net.Mail.Attachment(contentStrea, fileName[i].Trim());
mailMessage.Attachments.Add(attachment);
i++;
}
}
mailMessage.From = new MailAddress(ConfigurationManager.AppSettings["SenderMailAddress"]);
mailMessage.IsBodyHtml = true;
status = await SMTPCredentials(mailMessage);
return status;
}
See email screenshot here
I'm not sure, but try seeking the start of the stream before adding the attachment.
contentStrea.Seek(0, System.IO.SeekOrigin.Begin);
System.Net.Mail.Attachment attachment;
attachment = new System.Net.Mail.Attachment(contentStrea, fileName[i].Trim());

Send email using Html template with MailKit in .net core application

I am sending an email from .net core application using MailKit, and it will sent it successfully.
But I want to use HTML template to send email with MailKit in .Net core.
Here are the code currently sending email with static body part
var emailMessage = new MimeMessage();
if (!string.IsNullOrWhiteSpace(cc))
{
emailMessage.Cc.Add(new MailboxAddress(cc));
}
else if (!string.IsNullOrWhiteSpace(EmailUserNameCC))
{
emailMessage.Cc.Add(new MailboxAddress(EmailUserNameCC));
}
if (!string.IsNullOrWhiteSpace(EmailUserNameBCC))
{
emailMessage.Bcc.Add(new MailboxAddress(EmailUserNameBCC));
}
emailMessage.From.Add(new MailboxAddress(mailFrom));
emailMessage.To.Add(new MailboxAddress(mailTo));
emailMessage.Subject = subject;
if (!string.IsNullOrWhiteSpace(replyTo))
{
emailMessage.InReplyTo = replyTo;
}
var builder = new BodyBuilder();// { TextBody = message };
builder.HtmlBody = message;
if (attachments != null && attachments.Count > 0)
{
foreach (var item in attachments)
{
builder.Attachments.Add(item.Key, item.Value);
}
builder.HtmlBody = builder.HtmlBody + " \n" + " PFA";
}
var multipart = new Multipart("mixed");
multipart.Add(new TextPart("html") { Text = message });
emailMessage.Body = builder.ToMessageBody();
using (var client = new SmtpClient())
{
var credentials = new NetworkCredential
{
UserName = EmailUserName,
Password = EmailPassword
};
if (!client.IsConnected)
{
client.Connect(SmtpHost, Convert.ToInt32(EmailHostPort));
client.Authenticate(EmailUserName, EmailPassword);
}
client.MessageSent += c_EmailReached;
client.Send(emailMessage);
}
Now, I want to use HTML template to replace body part.
So how can I use HTML Template with MailKit in .Net Core ?
Additional:
-> Also the special characters are not showing in actual email after sending email with html template. For some special characters it is displaying � . So how can I resolved this, to show special characters also.
Thanks.
You can use StreamReader to read the source file and assign it to your builder.HtmlBody.
using (StreamReader SourceReader = System.IO.File.OpenText(path to your file))
{
builder.HtmlBody = SourceReader.ReadToEnd();
}

Categories

Resources