File locked after sending it as attachment - c#

I am sending a file as an attachment:
// Create the file attachment for this e-mail message.
Attachment data = new Attachment(filePath, MediaTypeNames.Application.Octet);
// Add time stamp information for the file.
ContentDisposition disposition = data.ContentDisposition;
disposition.CreationDate = System.IO.File.GetCreationTime(filePath);
disposition.ModificationDate = System.IO.File.GetLastWriteTime(filePath);
disposition.ReadDate = System.IO.File.GetLastAccessTime(filePath);
// Add the file attachment to this e-mail message.
message.Attachments.Add(data);
And then I want to move the file to another folder, however when I try to do this
try
{
//File.Open(oldFullPath, FileMode.Open, FileAccess.ReadWrite,FileShare.ReadWrite);
File.Move(oldFullPath, newFullPath);
}
catch (Exception ex)
{
}
Its throwing an exception that the file is already being used in another process. How I can unlock this file so that it can be moved to this location?

Disposing your message will fix this for you. Try calling Dispose on your message before moving the file, like so:
message.Dispose();
File.Move(...)
When disposing MailMessage, all locks and resources are released.

You need to take advantage of "using" keyword:
using(Attachment att = new Attachment(...))
{
...
}
That's because "using" ensures IDisposable.Dispose method is called at the end of code block execution.
Whenever you use some class that's managing some resource, check if it's IDisposable and then use "using" block and you won't need to care about manually disposing calling IDisposable.Dispose.

Attachments are IDisposable and should be disposed of correctly after they have been sent to release the lock on the file

In order to prevent this file lock from happening, you could use using as this will dispose of the object automatically:
using (Attachment data = new Attachment(filePath, MediaTypeNames.Application.Octet))
{
// Add time stamp information for the file.
ContentDisposition disposition = data.ContentDisposition;
disposition.CreationDate = System.IO.File.GetCreationTime(filePath);
disposition.ModificationDate = System.IO.File.GetLastWriteTime(filePath);
disposition.ReadDate = System.IO.File.GetLastAccessTime(filePath);
// Add the file attachment to this e-mail message.
message.Attachments.Add(data);
// Add your send code in here too
}

Here's another way to dispose of the Attachments, once you've finished with them...
// Prepare the MailMessage "mail" to get sent out...
await Task.Run(() => smtp.Send(mail));
// Then dispose of the Attachments.
foreach (Attachment attach in mail.Attachments)
{
// Very important, otherwise the files remain "locked", so can't be deleted
attach.Dispose();
}

A shared similar problem. I had to use image.dispose() on a given image before I could do something with the file that the image was created from.

Related

How to delete any dynamically created file from solution?

I am creating a file into the solution's directory as below:
var targetPathToCreate = Path.Combine(HttpRuntime.AppDomainAppPath, "FolderName");
DirectoryInfo dirInfo = new DirectoryInfo(targetPathToCreate);
if (!dirInfo.Exists)
{
dirInfo.Create();
}
var filePath = Path.Combine(targetPathToCreate, "FileName");
System.IO.File.WriteAllBytes(filePath, bytesArray);
and further I am sending this document via email and then trying to delete this document.
For that I tried this code:
System.IO.File.Delete("FilePath");
but this line throws an exception:
The process cannot access the file because it is being used by another process
Is there any way to delete the files by overcoming the exception?
You can use garbage collector's WaitForPendingFinalizers function.
It will suspend the current thread until the thread that is processing the queue of finalizers has emptied that queue.
if (System.IO.File.Exists(filePath))
{
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
System.IO.File.Delete(filePath);
}
If this doesn't work, you have to dispose your mail object.
mail.Attachments.Add(...);
smtpServer.Send(mail);
mail.Dispose();
System.IO.File.Delete(filePath);
I took a look and noticed that the error is possibly generated because your code System.IO.File.WriteAllBytes(filePath, bytesArray); may not be finished in its execution.
Here is a resource you can look at. You could also get the newly generated file's name by writing a new method that mails it and then deletes it, which could include something like this:
System.IO.FileInfo fi = new System.IO.FileInfo(#"C:\MyFiles\" + yourfilename + "'");
try
{
fi.Delete();
}

Deleting files gives an error (The process cannot access the file..)

I'm currently debugging my code because it gives me an error:
The process cannot access the file because it is being used by another process.
And i think that the error occurs in this lines of code
foreach (var filename in filenames)
{
var file = Path.Combine(filePath, filename);
mail.Attachments.Add(new Attachment(file));
}
// Send Mail
smtpServer.Send(mail);
DeleteFiles();
I want to delete the files in the folder when the mail is sent using this method
private void DeleteFiles()
{
string filePath = Server.MapPath("~/Content/attachments");
Array.ForEach(Directory.GetFiles(filePath), System.IO.File.Delete);
}
I read about closing/disposing? FileStream and etc. but how can i use that in my code? Thanks in advance.
mail.dispose(); You should dispose mail before deleting the file. This should remove the lock on the file.
foreach (var filename in filenames)
{
var file = Path.Combine(filePath, filename);
mail.Attachments.Add(new Attachment(file));
}
// Send Mail
smtpServer.Send(mail);
mail.Dispose();
DeleteFiles();
https://msdn.microsoft.com/en-us/library/0w54a951(v=vs.110).aspx
using(FileStream stream = new FileStream("thepath"))
{
//do stuff with the file
stream .Close();
}
Now the stream will be closed and disposed.

Using multiple memory streams for e-mail attachments

I'm trying to construct an EmailMessage using multiple in-memory streams. However, when I send the e-mail, I get the following error:
"One of the streams has already been used and can't be reset to the origin"
From what I can gather, I think the problem may be that the message is losing context of the memory stream when I try to do the following:
foreach (var att in attachments)
{
doc = fetchDocumentByteArray();
using (MemoryStream ms = new MemoryStream(doc))
{
mailToSend.AddAttachment(new Attachment(ms, att.Filename));
}
}
mailToSend.Send();
I've also tried setting the ms.Position = 0 before the AddAttachment(), but that doesn't work.
After looking around a bit for a scenario like mine, I came across a suggestion to use a List<MemoryStream> - but I'm not sure how I'd implement this or if it's even the correct approach?
When you use "using" keywork, internally, Dispose method is invoked. It will make the MemoryStreams be deallocated.
Remove the using in your inner loop and create a try/finally clause to dispose the memory streams after the e-mail is sent.
The accepted answer did not work for me because you need to keep track of multiple MemoryStreams and dispose of each individually. I took the same approach but implemented it slightly differently adding each MemoryStream to a list and then calling Dispose() on each instance after the email is sent.
var msList = new List<MemoryStream>();
foreach (var attachment in message.Attachments)
{
var ms = new MemoryStream(attachment.Bytes);
msList.Add(ms);
var mailAttachment = new Attachment(ms, attachment.FileName);
mailMessage.Attachments.Add(mailAttachment);
}
smtp.Send(mailMessage);
foreach (var ms in msList)
{
ms.Dispose();
}

Attach document to an email

Is it possible to attach a document to an email without saving it on the server?
The code below is something that attaches only after saving to the server. What I am looking for is to attach the document to the email without having it saved first instead just attach to the email from the path provided.
This is in Visual Studio 2005 using c#
if (SaveDocument.HasFile)
{
/* Get a reference to PostedFile object */
string strFileName = Path.GetFileName(SaveDocument.PostedFile.FileName);
/* Save the file on the server */
SaveDocument.PostedFile.SaveAs(Server.MapPath(strFileName));
/* Create the email attachment with the uploaded file */
System.Net.Mail.Attachment attach = new System.Net.Mail.Attachment(Server.MapPath(strFileName));
/* Attach the newly created email attachment */
message.Attachments.Add(attach);
}
Sure, there's a constructor overload for Attachment which accepts a Stream instead of a file name. So, for example, if you have a byte[] of data, you can create an Attachment from it:
var contentType new ContentType(MediaTypeNames.Text.Plain);
var attach = new Attachment(new MemoryStream(data), contentType);
Yes, just use the constructor that takes a stream rather than a filename.
See:
http://msdn.microsoft.com/en-us/library/ab7hb4y5(v=vs.110).aspx
Pass the PostedFile's InputStream directly to the Attachment.
if (SaveDocument.HasFile)
{
/* Create the email attachment with the uploaded file */
System.Net.Mail.Attachment attach = new System.Net.Mail.Attachment(SaveDocument.PostedFile.InputStream, "filename");
/* Attach the newly created email attachment */
message.Attachments.Add(attach);
}
I haven't tried it yet, but it should work.

File are corrupted when Attaching them to MailMessage C#

I have created an application at work that generates exel files from some database data. After generating the files they are sent automatically to the customers in question. My problem is that it works fine when i run the published application. But some users when they run the application the files are generated perfectly as they are saved on the HDD and i can see them. But when they are attached to the MailMessage object they get corrupted. This is an image of the corrupted files. These files should be Excel files.
This is my code for sending a mail with attached files:
public void SendMailedFilesDK()
{
string[] sentFiles = Directory.GetFiles(sentFilesDK);
if (sentFiles.Count() > 0)
{
using (System.Net.Mail.SmtpClient client = new System.Net.Mail.SmtpClient("ares"))
{
using (System.Net.Mail.MailMessage msg = new System.Net.Mail.MailMessage())
{
msg.From = new MailAddress("system#mail.dk");
msg.To.Add(new MailAddress("operation#mail.dk"));
msg.To.Add(new MailAddress("bl#mail.dk"));
msg.CC.Add("lmy#mail.dk");
msg.CC.Add("ltr#mail.dk");
msg.Subject = "IBM PUDO";
msg.Body = sentFiles.Count() + " attached file(s) has been sent to the customer(s) in question ";
msg.IsBodyHtml = true;
foreach (string file in sentFiles)
{
Attachment attachment = new Attachment(file);
msg.Attachments.Add(attachment);
}
client.Send(msg);
}
}
}
}
Why are the files getting corrupted when others run the application? We are all using office 2010.
You should make sure to set the content type of the attachement to the appropriate value.
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet for xlsx files, or
application/vnd.ms-excel for xls files.
For example, your loop should look something like this.
ContentType xlsxContent = new ContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
foreach (string file in sentFiles)
{
Attachment attachment = new Attachment(file, xlsxContent);
msg.Attachments.Add(attachment);
}
We use this in our Attachment constructor and have no issues attaching Excel and PDF.
Attachment data = new Attachment(sFileName, MediaTypeNames.Application.Octet);
Also check that the users running this have permissions to access the files in whatever location is specified by sentFilesDK.
You might want to specify the mimetype which is part of one of the constructors on Attachment class.
public Attachment(string fileName, ContentType contentType);
You can also read the file in memorystream and pass it as part of the following constructor.
public Attachment(Stream contentStream, string name, string mediaType);

Categories