StreamWriter can't open file because of a process - c#

I got the following code
protected override void Render(HtmlTextWriter writer)
{
// Write the HTML into this string builder
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
HtmlTextWriter hWriter = new HtmlTextWriter(sw);
base.Render(hWriter);
string pageHTML = sb.ToString();
// Write it back to the server
writer.Write(pageHTML);
if (Convert.ToBoolean(this.ViewState["SendEmail"]))
{
string HTML = "";
HTML = "<!DOCTYPE HTML PUBLIC '-//IETF//DTD HTML//EN'>";
HTML += "<html>";
HTML += "<head>";
HTML += "<meta http-equiv='Content-Type'";
HTML += "content='text/html; charset=iso-8859-1'>";
HTML += "<title>Order Information</title>";
HTML += "</head>";
HTML += "<body>";
HTML += "See attachment for information.";
HTML += "</body>";
HTML += "</html>";
MailMessage mail = new MailMessage("from#xxx.com", "to#xxx.com", "Subject", HTML);
mail.IsBodyHtml = true;
string path = #"d:\websites\plate.html";
using (StreamWriter sw11 = File.CreateText(path))
{
sw11.WriteLine(pageHTML);
}
mail.Attachments.Add(new Attachment(path));
SmtpClient client = new SmtpClient("192.168.1.127");
client.Send( mail );
Response.Write("<script>alert('Your information has been sent.')</script>");
this.ViewState["SendEmail"] = false;
}
}
After a fresh clean/build of my solution, when I press the send button, this function is called and the html page is sent in attachment by mail without a problem. But if I try to press again the send button, I'm getting "System.IO.IOException: The process cannot access the file 'd:\websites\plate.html' because it is being used by another process." The error occur when I'm trying to open the file. What's wrong?

SmtpClient implements IDisposable but you are not disposing the instance.
http://msdn.microsoft.com/en-us/library/system.net.mail.smtpclient.aspx
It may be holding on to the file for that reason.
Generally speaking, it is wise to wrap anything that implements IDisposable in a using statement unless you have a specific reason not to (e.g. your are explicitly managing the object lifetime via the class that holds an IDisposable instance).
I also want to draw attention to #DanPichelman's comment that you are using a constant file name, but this code may execute on separate threads in parallel. That would cause the output file to be locked for any user past the first user, until the code completes for the first user.

As Eric has pointed out, you should have the SmtpClient in a using statement - ditto MailMessage.
However, you'd still end up writing to the file system for no obvious reason. I'd strongly advise you to use one of the Attachment constructors which doesn't require a file to start with. You can write to a MemoryStream, rewind it, and then provide that to the Attachment for example.
Aside from anything else, that would mean you wouldn't have a problem if multiple threads (or processes) tried running this code at the same time.

I think you should close it:
using (StreamWriter sw11 = File.CreateText(path))
{
sw11.WriteLine(pageHTML);
sw11.Flush();
sw11.Close();
}

Related

JSON Files old data overwritten when opening a new connection c#

I have a c# WPF Solution. The solution needs to add an object to a JSON file. Which works. The problem i have is when opening a new connection to the file the old data is overwritten. What am i missing? why does it overwrite the old data. am pretty sure it's something simple. but i just can't see it.
if (clickCount == 1)
{
//changes create button text
createBtn.Content = "Create";
//creates new message obj
Message message = new Message();
//depending on the form type creates a json object
if (valid.MessageType == "E")
{
//checks e-mail
valid.CheckEmail(senderTxtBox.Text);
//creates varibles for adding to JSON file
message.MessageId = messageTypeComboBox.Text + messageTypeTxtBox.Text;
message.SenderTxt = senderTxtBox.Text;
message.Subject = subjectTxtBox.Text;
message.MessageTxt = messageTxtBox.Text;
}
messageList.Add(message);
String json = JsonConvert.SerializeObject(messageList, Formatting.Indented);
System.IO.File.WriteAllText(#"JsonMessage.Json", json);
clickCount = 0;
messageTxtBox.Clear();
senderTxtBox.Clear();
subjectTxtBox.Clear();
messageTxtBox.Clear();
messageTypeTxtBox.Clear();
messageTypeComboBox.SelectedIndex = -1;
}
You are using WriteAllText, which will rewrite the file every time you want to transmit a new Json object to your json file.
File.AppendText seems like a better solution, since you would not need to actually rewrite all the file, whenever a new message is added to your MessageList, but would also solve your existing problem when you open a new connection to the file not having all the previously inserted json data deleted.
PS. If you use AppendText, you will have to pass to the file all your collection, but only the message that you just received, otherwise you would have your file constantly being written with duplicated data and the situation would only get worse with the increase in size of your message list object.
I see that you've used the WriteAllText method to write the json to the file.
You should use AppendText method instead.
Something like this
using (StreamWriter sw = System.IO.File.AppendText(#"JsonMessage.Json"))
{
sw.WriteLine(json);
}
Here's a link for more information on File.AppendText
https://msdn.microsoft.com/en-us/library/system.io.file.appendtext(v=vs.110).aspx

How to make 2 process access the same path?

I'm using c#. I'm receiving an error about a path is currently accessed by other processes. What my system is trying to do is to access the path: #"C:\temps\" + client_ids + "_" + rown + ".pdf" and use the same path for attachment before sending it to client's email.
here's what I've done so far. I comment out some of my code because I'm not sure what to do.
FileStream fs = null;
using (fs = new FileStream(#"C:\\temps\\" + client_ids + "_" +
rown + ".pdf",
FileMode.Open,FileAccess.Read,FileShare.ReadWrite))
{
TextReader tr = new StreamReader(fs);
//report.ExportToDisk
//(CrystalDecisions.Shared.ExportFormatType.PortableDocFormat,tr);
//report.Dispose();
//Attachment files = new Attachment(tr);
//Mailmsg.Attachments.Add(files);
//Clients.Send(Mailmsg);
}
you can make temp copy of file before you use it in mail attachment and then use the copy instead of the original file
You cannot attach a file to an email if that file is open. You must close (save) the file first.
While #ali answer is technically correct, it is unnecessary. Why go through the overhead of creating a copy of the file which then needs to be deleted, etc.?
Assuming I understand what you are trying to do correctly, simply move your code for mail to after the file is successfully created and saved. And, I don't think you need the overhead of either the filestream or the textreader. As long as your report object can save the file to disk someplace, you can attach that file to your email message and then send it.
While I do not claim to know anything about how Crystal Decisions handles exports, etc. Perhaps something like this would work:
(I got this code from: https://msdn.microsoft.com/en-us/library/ms226036(v=vs.90).aspx)
private void ExportToDisk (string fileName)
{
ExportOptions exportOpts = new ExportOptions();
DiskFileDestinationOptions diskOpts =
ExportOptions.CreateDiskFileDestinationOptions();
exportOpts.ExportFormatType = ExportFormatType.RichText;
exportOpts.ExportDestinationType =
ExportDestinationType.DiskFile;
diskOpts.DiskFileName = fileName;
exportOpts.ExportDestinationOptions = diskOpts;
Report.Export(exportOpts);
}
You will need to change the ExportFormatType property.
Then, simply attach the file to your email and send:
Attachment Files = new Attachment(filename);
Mailmsg.Attachments.add(files);
Clients.Send(Mailmsg);

PDF creation and email on clicking submit

I have a checkbox list for the products to selected, with another text entry for quantity. Upon clicking Submit, I need two actions to be performed:
1) I would like to get the customers selection and entry into an email (with a given email address) and emailed as a (create) PDF attachment with date-time stamp.
2) The next page after hitting submit button, should produce next/another page with email confirmation message and a print button of the PDF attachment with date-time stamp.
There are many other things that go into this webform, but this is the area that I am needing some guidance and any link or code example to help me find a proper solution you can reply me here would be much appreciated.
For PDF creation, there are several tools- I've used iTextSharp. You can download the nuget package on VS and example code can be found here. You will probably want to use tables to make it look nice, but to get used to it, maybe just throw some info in to get an idea of how it works.
The method may look something like this:
public byte[] CreateOrderPdf(OrderDetails details)
{
byte[] fileBytes;
using (MemoryStream PdfStream = new MemoryStream())
{
iTextSharp.text.Document doc = new iTextSharp.text.Document();
PdfWriter writer = PdfWriter.GetInstance(doc, PdfStream);
doc.Open();
doc.Add(new Paragraph(OrderDetails.Name));
doc.Add(new Paragraph(OrderDetails.Address));
//Add More Tables and Content Here (See Documentation for more)
doc.Close();
fileBytes = PdfStream.ToArray();
}
return fileBytes
}
For Email, .Net has the System.Net.Mail.MailMessage class. You can attach the pdf as a stream. You will need to define the to, from, subject, body, etc. That would look something like this:
string fileName = string.Format("OrderConfirmation-{0}.pdf", DateTime.Now.ToString("yyyy-dd-M--HH-mm-ss"));
MailMessage message = new MailMessage(from, to);
message.Subject = subject;
message.Body = body;
MemoryStream stream = new MemoryStream(fileBytes);
stream.Position = 0;
Attachment attachment = new Attachment(stream, fileName, MediaTypeNames.Application.Octet);
message.Attachments.Add(attachment);
SmtpClient mailServer = new SmtpClient(example.stmphost.com);
mailserver.Send(message);
message.Dispose();
Hopefully this is a good starting point for you.
In my search of the solution and suggested starting point, I stumbled upon this and I will also MENTION this for anyone looking for the same answers to look into spire.pdf at www.e-iceblue.com/Introduce/pdf-for-net-introduce.html

Clear current attachment's data and input new data everytime I send email

Currently my email function is working and is able to send an attachment every time I click the button.
However every time I send an email, the data is just added to what is already in the file.
I was just wondering if it is possible to clear the test.html data inside then input the new data inside.
string attachmentBody = "Testing 1 2 3";
StreamWriter sw = new StreamWriter("Test.html", true, Encoding.UTF8);
sw.Write(attachmentBody);
sw.Close();
ttachment data = new Attachment("Test.html");
mailObj.Attachments.Add(data);
Problem is that you're passing true to append param (take a look here for StreamWriter constructor syntax).
Try this:
StreamWriter sw = new StreamWriter("Test.html", false, Encoding.UTF8);

SMTP Send is locking up my files - c#

I have a function thats sending messages ( a lot of them) and their attachments.
It basically loops through a directory structure and creates emails from a file structure for example
c:\emails\message01
\attachments
c:\emails\message02
\attachments
The creation of the messages takes place using .net c#, standard stuff.
After all messages are created... I have another function that runs directly afterwards that copies the message folder to another location.
Problem is - files are locked...
Note: I'm not moving the files, just copying them....
Any suggestions on how to copy locked files, using c#?
Update
I have this add attachments method
private void AddAttachments(MailMessage mail)
{
string attachmentDirectoryPath = "c:\messages\message1";
DirectoryInfo attachmentDirectory = new DirectoryInfo(attachmentDirectoryPath);
FileInfo[] attachments = attachmentDirectory.GetFiles();
foreach (FileInfo attachment in attachments)
{
mail.Attachments.Add(new Attachment(attachment.FullName));
}
}
How are you reading the files to create the email message? They should be opened as read-only, with a FileShare set to FileShare.ReadWrite... then they shouldn't be locked. If you are using a FileStream you should also wrap your logic in the using keyword so that the resource is disposed properly.
Update:
I believe disposing the mail message itself will close resources within it and unlock the files:
using (var mail = new MailMessage())
{
AddAttachments(mail);
}
// File copy code should work here
hate answering my own post, but yeah for the next poor guy who has this problem here is the fix:
AFTER YOU SEND THE MESSAGE
// Send the mail
client.Send(message);
//Clean up attachments
foreach (Attachment attachment in message.Attachments)
{
attachment.Dispose();
}
Dispose the attachments... clears the lock, and messages will still be sent with attachments. Dispose DOES NOT delete the files, just clears the attachments :)
Are you closing the files after you finish reading them? If you open them for reading, but don't close them when you're done, it should keep a lock on it until the program exits and automatically closes all the files.
MailMessage email = new MailMessage();
email.From = txtFrom.Text;
email.To = txtToEmail.Text;
email.Subject = txtMSubject.Text;
email.Body = txtBody.Text;
SmtpClient mailClient = new SmtpClient();
mailClient.Host = "smtp.emailAddress";
mailClient.Port = 2525;
mailClient.Send(email );
email.Dispose();
// After Disposing the email object you can call file delete
if (filePath != "")
{
if (System.IO.File.Exists(filePath))
{
System.IO.File.Delete(filePath);
}
}
I see this a lot when sending attachments. I normally use something like the following:
In the code that moves the files to a different location, you can use the following pattern:
Inside the loop for looping through the files
bool FileOk = false;
while (!FileOk)
{
try
{
// code to move the file
FileOk = true;
}
catch(Exception)
{
// do nothing or write some code to pause the thread for a few seconds.
}
}

Categories

Resources