I generate a PDF file, save it on the server:
var bytes = ms.ToArray();
. . .
String fileFullpath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), pdfFileName);
. . .
File.WriteAllBytes(fileFullpath, bytes);
...and then save it into a Sharepoint Document Library, and send it as an email attachment to the person who generated the file:
SavePDFToDocumentLibrary(fileFullpath);
String from = GetFromEmailID();
String to = GetUserEmail();
String subjLine = String.Format("The PDF file you generated ({0})", pdfFileName);
String body = String.Format("The Direct Pay PDF file you generated ({0}) is attached.", pdfFileName);
SendEmailWithAttachment(fileFullpath, from, to, subjLine, body);
// Now that it has been put in a Document Library and emailed, delete the file that was saved locally
File.Delete(fileFullpath);
...at which point, I no longer need the file I saved to disk on the server, and so, as shown in the last line above, attempt to delete it.
However, it doesn't work. The now-redundant file is still in the place where it was saved.
Why, and how can I get it to understand that "Delete" really means "delete"?
UPDATE
Here are the methods Scott wanted to see:
// This works; got it from Henry Zucchini's answer at http://stackoverflow.com/questions/468469/how-do-you-upload-a-file-to-a-document-library-in-sharepoint
private void SavePDFToDocumentLibrary(String fullpath)
{
String fileToUpload = fullpath;
String sharePointSite = siteUrl;
String documentLibraryName = "DirectPayPDFForms";
using (SPSite oSite = new SPSite(sharePointSite))
{
using (SPWeb oWeb = oSite.OpenWeb())
{
if (!System.IO.File.Exists(fileToUpload))
throw new FileNotFoundException("File not found.", fileToUpload);
SPFolder doclib = oWeb.Folders[documentLibraryName];
// Prepare to upload
Boolean replaceExistingFiles = true;
String fileName = System.IO.Path.GetFileName(fileToUpload);
FileStream fileStream = File.OpenRead(fileToUpload);
// Upload document
SPFile spfile = doclib.Files.Add(fileName, fileStream, replaceExistingFiles);
// Commit
doclib.Update();
}
}
}
// This is adapted from https://msdn.microsoft.com/en-us/library/system.net.mail.mailmessage(v=vs.90).aspx
public static void SendEmailWithAttachment(string fileToMail, String from, String to, String subj, String body)
{
String server = GetSMTPHostName(); //"468802-DEV-SPWF"; // change this to prod when go live, or programatically assign?
// Specify the file to be attached and sent.
string file = fileToMail;
// Create a message and set up the recipients.
MailMessage message = new MailMessage(
from,
to,
subj,
body);
// Create the file attachment for this e-mail message.
Attachment data = new Attachment(file, MediaTypeNames.Application.Octet);
// Add time stamp information for the file.
ContentDisposition disposition = data.ContentDisposition;
disposition.CreationDate = System.IO.File.GetCreationTime(file);
disposition.ModificationDate = System.IO.File.GetLastWriteTime(file);
disposition.ReadDate = System.IO.File.GetLastAccessTime(file);
// Add the file attachment to this e-mail message.
message.Attachments.Add(data);
//Send the message.
SmtpClient client = new SmtpClient(server);
// Add credentials if the SMTP server requires them.
client.Credentials = CredentialCache.DefaultNetworkCredentials;
try
{
client.Send(message);
}
catch (Exception ex)
{
Console.WriteLine("Exception caught in CreateMessageWithAttachment(): {0}", ex.ToString());
}
// Display the values in the ContentDisposition for the attachment.
// May not need/want this section
ContentDisposition cd = data.ContentDisposition;
Console.WriteLine("Content disposition");
Console.WriteLine(cd.ToString());
Console.WriteLine("File {0}", cd.FileName);
Console.WriteLine("Size {0}", cd.Size);
Console.WriteLine("Creation {0}", cd.CreationDate);
Console.WriteLine("Modification {0}", cd.ModificationDate);
Console.WriteLine("Read {0}", cd.ReadDate);
Console.WriteLine("Inline {0}", cd.Inline);
Console.WriteLine("Parameters: {0}", cd.Parameters.Count);
foreach (DictionaryEntry d in cd.Parameters)
{
Console.WriteLine("{0} = {1}", d.Key, d.Value);
}
// </ May not need/want this section
data.Dispose();
}
UPDATE 2
I see when stepping through it, after adding this test:
if (File.Exists(fileFullpath))
{
File.Delete(fileFullpath);
}
...that there is an exception after all, in the IOException catch block:
The process cannot access the file 'C:\Users\TEMP.SP.018\Desktop\DirectPayDynamic_2015Jul28_19_02_clayshan_0.pdf' because it is being used by another process.
So how is one of the other methods holding onto it? ISTM that SavePDFToDocumentLibrary() is safe, because it uses using blocks.
Is data.Dispose(); in SendEmailWithAttachment() not enough? Do I need to explicitly call close there, or what?
UPDATE 3
I added "message.Dispose();" just prior to "data.Dispose();" in SendEmailWithAttachment(), but it made no difference.
Try disposing the file stream used in SavePDFToDocumentLibrary like so:
using (FileStream fileStream = File.OpenRead(fileToUpload))
{
...
}
Related
This is about sending/receiving contents of an XML file to/from Azure Service Message Queue using TopicClient in C#.
I am sending contents of a XML file as string, I can see the message on the Azure Queue and can even read its contents properly, so everything works fine when I send a plain text XML file.
But, due to data restrictions on the incoming message on the queue, I had to compress the file before sending it, I am using the C#'s DeflateStream to compress the contents of the file and writing it back to a file. On the receiving end, I am able to read the contents of the file but its not the same as what was sent.
I suspect this is has got something to do with the encoding. Could you please guide me on what I am missing? Thanks in advance.
Sender
public string Compress(FileInfo XMLFile) {
using(FileStream originalFileStream = file.OpenRead()) {
if ((File.GetAttributes(file.FullName) & FileAttributes.Hidden) !=
FileAttributes.Hidden & file.Extension != ".cmp") {
using(FileStream compressedFileStream = File.Create(file.FullName + ".cmp")) {
using(DeflateStream compressionStream = new DeflateStream(compressedFileStream, CompressionMode.Compress)) {
originalFileStream.CopyTo(compressionStream);
}
}
FileInfo info = new FileInfo(directoryPath + "\\" + file.Name + ".cmp");
Console.WriteLine("Compressed {0} from {1} to {2} bytes.", file.Name, file.Length, info.Length);
}
}
return info.FullName;
}
// snippet from the send method
FileInfo XMLfile = new FileInfo(XMLFilePath);
string CompressedXMLFilePath = Compress(XMLfile);
TopicClient myTopicClient = TopicClient.CreateFromConnectionString(AzureConnectionString);
string toSend = File.ReadAllText(CompressedXMLFilePath); // read contents of file compressed with DeflateStream.
myTopicClient.Send(new BrokeredMessage(toSend));
Receiver
var subClient = SubscriptionClient.CreateFromConnectionString(_serviceBusConn, _serviceBustopic, "<subscription name>");
subClient.OnMessage(m =>
{
Console.WriteLine(m.GetBody<string>());
});
I am working on code in C#,but not going through on thing that is I have saved some .eml file on my disk now I am parsing each eml file and creating a new mail adding the eml file data to the new mail but I am unable to attach the attachments present in the .eml file to the new mail , can anybody please help?
I am using the follwing code but it shows the error ex = {"The process cannot access the file because it is being used by another process.\r\n":null}
foreach (CDO.IBodyPart attach in msg.Attachments)
{
i++;
string filenm = "C:\\mail_automation\\attachments\\xyz" + i +".eml";
if (File.Exists(filenm))
{
string fn = attach.FileName;
attach.SaveToFile("C:\\mail_automation\\attachments\\xyz" + i + ".eml");
Attachment data = new Attachment(filenm);
mailMessage.Attachments.Add(data);
}
else
{
File.Create(filenm);
string fn = attach.FileName;
attach.SaveToFile("C:\\mail_automation\\attachments\\xyz" + i + ".eml");
Attachment data = new Attachment(filenm);
mailMessage.Attachments.Add(data);
}
You have to extract the attachments and save them to disk first, then fetch it again into the new mail.
Code example here
MailMessage message = new MailMessage();
MemoryStream ms = new MemoryStream(); //store the mail into this ms 'memory stream'
ms.Position = 0;
message.Attachments.Add(new Attachment(ms, attachmentName));
I am trying to send an email with an attachment by accessing it directly after saving it in the database. To do so I am following this tutorial.
What works?
Storing the attachments in the database is correct as when I go to the details page I can see the image associated with the profile.
What doesn't?
Unfortunately there seems to be a problem with how retrieving files from database works as the attachments are damaged, e.g. image stored in database shows 153328 B, but when sent turns into 117B).
The solution that actually succeeds and sends an email (damaged email) is taken from this link, but when I try to send it using the commented out stream code, the code crashes on the indicated line:
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing); //this line
}
this is the controller code i use to save and retrieve the attachments:
public async Task<ActionResult> Create([Bind(Include = "ID,LastName,FirstMidName")] Person person, HttpPostedFileBase upload)
{
if (ModelState.IsValid)
{
if (upload != null && upload.ContentLength > 0)
{
var avatar = new File
{
FileName = System.IO.Path.GetFileName(upload.FileName),
FileType = FileType.Avatar,
ContentType = upload.ContentType
};
using (var reader = new System.IO.BinaryReader(upload.InputStream))
{
avatar.Content = reader.ReadBytes(upload.ContentLength);
}
person.Files = new List<File> { avatar };
}
db.People.Add(person);
db.SaveChanges();
//await SendEmail(person.ID);
var message = new MailMessage();
var file = db.Files.Find(person.ID);
Attachment attachment;
var stream = new MemoryStream();
try
{
stream.Write(file.Content, 0, file.Content.Length - 1);
attachment = new Attachment(stream, file.FileName);
}
catch
{
stream.Dispose();
throw;
}
//When i use this bit of code, I receive an error "Cannot access a closed stream
//using (var stream = new MemoryStream())
//{
// stream.Write(file.Content, 0, file.Content.Length - 1);
// attachment = new Attachment(stream, file.FileName);
//}
var fileSize = file.Content.Length;
message.Attachments.Add(attachment);
message.To.Add(new MailAddress("recipient#gmail.com")); // replace with valid value
message.From = new MailAddress("sender#outlook.com"); // replace with valid value
message.Subject = "Your email subject";
message.BodyEncoding = System.Text.Encoding.UTF8;
message.Body = "<p>file size: </p>" + "<p>" + fileSize + "</p>";
message.IsBodyHtml = true;
message.BodyEncoding = System.Text.Encoding.UTF8;
using (var smtp = new SmtpClient())
{
//when i try to send the mail asynchronously the view with the form just keeps showing "waiting for localhost"
//await smtp.SendMailAsync(message);
smtp.Send(message);
return RedirectToAction("Index");
}
}
return View(person);
}
Additional Question
Would it be a good idea to send the attachment inside of the save to database part?
EDIT
I have just tried sending the attachment with the below line of code:
message.Attachments.Add(new Attachment(upload.InputStream, Path.GetFileName(upload.FileName)));
added after:
person.Files = new List<File> { avatar };
But still receive damaged attachment..
EDIT 2:
I think this line
var file = db.Files.Find(person.ID)
should actually be (you were trying to get a file using a person id):
var file = db.Files.Find(avatar.ID)
but, in your case you don't need to retrieve it from the database. You already have the bytes there, so just wrap them in a MemoryStream, as you can't directly send the upload.InputStream without storing it in memory:
attachment = new Attachment(new MemoryStream(avatar.Content), file.FileName);
Looking at this quickly, I'd look at the obvious.
var file = db.Files.Find(person.ID);
Look at what this is returning. It may well be that after this object is being used, depending on what object it is, may have been disposed of already.
The reason being is you're attempting to read from the file.Content.Length which may be the very cause to the problem because it doesn't have a value or whatever.
Step through the logic, line by line. Break it down from the most simple, and build it up slowly until you get to the cause. Also, think about abstracting the logic from the controller and implementing a service that deals with this action. Check out repository pattern, unit of work and dependency injection as a side note.
Ultimately, your issue, I think it's just the fact that you're not checking all the "what if it wasn't the way you expected" type errors, which in all is why you should most probably also have some tests in place. :P
Deconstruct, start from basics and build your way up. Doing this, I'm sure you will find the problem. :)
I have the following code that basically attaches a file to an email message then after all attachments are attached and email is sent, i try to delete all files, however I get a file in use exception. I believe the error comes in this line
Attachment data = new Attachment(file, MediaTypeNames.Application.Octet);
I tried using this code but I get an cannot sent email message
using Attachment data = new Attachment(file, MediaTypeNames.Application.Octet)){
//and the rest of the code in here.
}
foreach (KeyValuePair<string, string> kvp in reports) {
browser.GoTo(kvp.Value);
Thread.Sleep(1000);
System.IO.File.Move(#"C:\Reports\bidata.csv", #"C:\Reports\"+kvp.Key.ToString()+".csv");
string file = #"C:\Reports\" + kvp.Key.ToString() + ".csv";
Attachment data = new Attachment(file, MediaTypeNames.Application.Octet);
// Add time stamp information for the file.
ContentDisposition disposition = data.ContentDisposition;
disposition.CreationDate = System.IO.File.GetCreationTime(file);
disposition.ModificationDate = System.IO.File.GetLastWriteTime(file);
disposition.ReadDate = System.IO.File.GetLastAccessTime(file);
// Add the file attachment to this e-mail message.
mail.Attachments.Add(data);
}
smtpserver.Send(mail);
string[] files = Directory.GetFiles(#"C:\Reports");
foreach (string files1 in files)
{
File.Delete(files1);
}
In order to delete the files first you will have to dispose the attachment and mail objects and then delete the files
Dispose the smtpclient by putting it in a usings or calling dispose directly. That should free the file resource and allow you to nuke it.
While reading a text file(which contains the location of a file to be exported to a database) using the streamReader function in C#, how can I add a confirmation message to the code that will be displayed in the command prompt window(console application) so that I know the file got read and was exported?
public class Script
{
public static void Main(string[] args)
{
// Prepare the type that will handle all of the exporting needs
FileExporter exporter = new FileExporter();
try
{
//create an instance of StreamReader to read from a file.
//The using statemen also closes the StreamReader.
using (StreamReader sr = new StreamReader("ScriptFile.txt"))
{
string filePath;
//read and display lines from the file until the end of
//the file is reached.
while ((filePath = sr.ReadLine()) != null)
{
// Throw error if file does not exists to terminate the process.
if (!File.Exists(filePath))
{
string msg = string.Format("File not found at {0}.", filePath);
throw new FileNotFoundException(msg);
}
// Set the name of the export to be the name of the file.
string exportName = new FileInfo(filePath).Name;
// Export image as an SHP file if the extension matches.
if (filePath.Contains(".shp"))
{
exporter.processSHP(filePath, exportName, "");
//need confirmation that exporter.processSHP occured <<<-----***
}
else
{
string fileExtension = filePath.Split('.')[filePath.Split('.').Length - 1];
exporter.processIMG(filePath, exportName, "", fileExtension);
//need confirmation that exporter.processIMG occured <<<-----***
}
}
}
}
catch (Exception e)
{
Console.WriteLine(
string.Format("Process terminated. An error has occurred: {0}", e.ToString()));
}
}
Add this:
Console.WriteLine("Done reading & Exporting");
above
}
catch (Exception e)
{
Don't forget the Console.ReadKey() in case you want to actually see it up there
use flush and then close on your writer object.
then write done to console.
After you read the file to the end and look for your match (assuming you have something like a boolean value to let you know the export happened and a match was found) you can check the EndOfStream property in the streamreader and output the message. Or you can just check your match value to see if it returned true.