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);
Related
I am using this code to read .msg file in C#
Microsoft.Office.Interop.Outlook.Application app = new Microsoft.Office.Interop.Outlook.Application();
var item = app.Session.OpenSharedItem("MSG_FILE_PATH") as Microsoft.Office.Interop.Outlook.MailItem;
string body = item.HTMLBody;
How can I use the same library to read .eml file?
According to my tests, the Outlook function "OpenSharedItem" just only used to open iCalendar appointment (.ics) files, vCard (.vcf) files, and Outlook message (.msg) files. For the eml file, I think you can refer to the following code:
protected CDO.Message ReadMessage(String emlFileName)
{
CDO.Message msg = new CDO.MessageClass();
ADODB.Stream stream = new ADODB.StreamClass();
stream.Open(Type.Missing, ADODB.ConnectModeEnum.adModeUnknown,
ADODB.StreamOpenOptionsEnum.adOpenStreamUnspecified, String.Empty, String.Empty);
stream.LoadFromFile(emlFileName);
stream.Flush();
msg.DataSource.OpenObject(stream, "_Stream");
msg.DataSource.Save();
return msg;
}
More information for this: Retrieve Email Information from .EML Files
Thanks,
Simon
I need to be able to send textual data stored in a database in multiple row format as txt file attachments in an email. Think Notes added to something by different people at different times.
As of right now the email sends and there are some text files generated like test0.txt and test1.txt but they're empty. I'm flushing the streamwriter so to me it seems like the file should have text in it. I can't close the stream writer before I send the email because then I get an error saying cannot read from closed stream. I'm storing both the memorystream and streamwriter in containers so they shouldn't get disposed of or cleaned up until the very end. I wonder if the email object gets lost or for some reason can't access the streams stored in the containers?
I've looked at some similar questions on SO but they don't seem to work.
This person is using byte[] so only memory stream no streamwriter
This person disposes of their streamwriter before sending email which errors out for me
Instead of trying to do this in memory should I just be writing temp files and then including those as attachments? It seems slow to write to disk just so that I can then read from disk to attach the attachment.
var companyEmail = new MailAddress("address#company.com", "Person Name");
email.To.Add(companyEmail);
email.From = new System.Net.Mail.MailAddress("donotreply#company.com", "doesn't matter");
email.Subject = "subject";
email.Body = "body";
email.IsBodyHtml = true;
var nonAttCounter = 0;
var nonAttStreamHolder = new List<MemoryStream>();
var nonAttWriterHolder = new List<StreamWriter>();
//churn through the attachments and see if any of them are checked in the form
foreach (DataRow datarow in claim.attachments.Rows)
{
string cbFormName = "ctl00$MainBody$att" + datarow["attNum"].ToString();//name of checkbox controls on page and in form.
var includedInForm = rForm[cbFormName];
//see if the attachment was selected as one to include.ie the attNum is in the posted form.
if (includedInForm != null)
{
string origData = datarow["origData"].ToString();
string[] fIDs = origData.Split(',');
foreach (var item in fIDs)
{
//not all attachments are real attachments with files...cause why would attachments be attachments.
int fid;
bool isInt = int.TryParse(item, out fid);
if (isInt)
{
var tempDS = new datastore(ref fid);
var tempData = tempDS.blobData;
email.Attachments.Add(new Attachment(tempData, tempDS.fileNameWithExt));
}
else
{
//grab all the textual data from the database for this "attachment" and write it to a memory stream and upload to the email
nonAttStreamHolder.Add(new MemoryStream());
nonAttWriterHolder.Add(new StreamWriter(nonAttStreamHolder[nonAttCounter]));
nonAttWriterHolder[nonAttCounter].WriteLine("This is a test.");
nonAttWriterHolder[nonAttCounter].WriteLine("Why this no work?!");
nonAttWriterHolder[nonAttCounter].Flush();
//nonAttWriterHolder[nonAttCounter].Close();
System.Net.Mime.ContentType ct = new System.Net.Mime.ContentType(System.Net.Mime.MediaTypeNames.Text.Plain);
System.Net.Mail.Attachment tempFile = new System.Net.Mail.Attachment(nonAttStreamHolder[nonAttCounter], ct);
tempFile.ContentDisposition.FileName = "test" + nonAttCounter + ".txt";
email.Attachments.Add(tempFile);
nonAttCounter++;
}
}
}
}
Global_Utilities.SharedFunctions.emailQuickSend(email);
foreach (var writer in nonAttWriterHolder)
{ writer.Close(); }
foreach (var stream in nonAttStreamHolder)
{ stream.Close(); }
You can simply take your string(s) and encode them as byte[], then use the techniques outlined in attach multiple files to an email programticaly without writing to disk. Here's some simple code to get you started:
var myString = "This is a test.";
var myBytes = System.Text.Encoding.UTF8.GetBytes(myString);
Now myBytes is an array of bytes. Note that you'll likely want to specify the encoding of the attachment, via the TransferEncoding property.
What I want to achieve:
Scan mails and attach relevant ones to a "summary"-mail.
My problem:
I can't seem to find any information about how this is done. When using for example Outlook you can simply drag and drop a mail into another message thus attaching it. I looked through the headers and found that it's basically the mail's content and attachments with their content types attached without further encoding. But attaching this data to a MailMessage via Attachment.CreateAttachmentFromString didn't work out either, the file was displayed as a regular file.
My current code:
var mail = new MailMessage(settings.Username, to);
var smtp = new SmtpClient(settings.SMTP, settings.Port);
// ...
// authentication + packing stuff into subject and body
// ...
foreach (var att in attachments)
{
Attachment attachment = Attachment.CreateAttachmentFromString(att.Text, att.Filename);
mail.Attachments.add(attachment);
}
client.Send(mail);
client.Dispose();
mail.Dispose();
My question:
Can C# do this out of the box using some hack or are there libraries that support that?
You would probably want to just use the Attachment constructor that takes a file name:
Attachment attachment = new Attachment(att.Filename);
mail.Attachments.add(attachment);
Of course, this assumes you've saved the attachment already out to your file system somewhere.
You could also just use the attachment's content stream to avoid the overhead of saving each attachment to file first:
Attachment attachment = new Attachment(att.ContentStream, String.Empty);
mail.Attachments.add(attachment);
NOTE: the second argument to that constructor is the "content type", which, if left as an empty string, will be text/plain; charset=us-ascii. Refer to RFC 2045 Section 5.1 for more content types.
Also, see MSDN for more Attachment constructor overloads: https://msdn.microsoft.com/en-us/library/System.Net.Mail.Attachment.Attachment%28v=vs.110%29.aspx
Well, I found a way to somehow does what I needed. This solution is not the perfect answer, but it works almost as intended.
Warning
This solution requires currently Outlook installed as the mail needs to be attached as a .msg file. I want to repeat that this is not the right way to go, this method is way slower than any other solution but it works. I will further investigate soon.
But for now, here's my Extension class:
using System;
using System.Net.Mail;
using System.IO;
using Outlook = Microsoft.Office.Interop.Outlook;
namespace MailAttachment
{
public static class Extensions
{
public static string AttachMail(this MailMessage mail, MailMessage otherMail)
{
string path = Path.GetTempPath(),
tempFilename = Path.Combine(path, Path.GetTempFileName());
Outlook.Application outlook = new Outlook.Application();
Outlook.MailItem outlookMessage;
outlookMessage = outlook.CreateItem(Outlook.OlItemType.olMailItem);
foreach (var recv in message.To)
{
outlookMessage.Recipients.Add(recv.Address);
}
outlookMessage.Subject = mail.Subject;
if (message.IsBodyHtml)
{
outlookMessage.BodyFormat = Outlook.OlBodyFormat.olFormatHTML;
outlookMessage.HTMLBody = message.Body;
}
else
{
outlookMessage.Body = message.Body;
}
outlookMessage.SaveAs(tempFilename);
outlookMessage = null;
outlook = null;
Attachment attachment = new Attachment(tempFilename);
attachment.Name = mail.Subject + ".msg";
otherMail.Attachments.Add(attachment);
return tempFilename;
}
}
}
Additional information
This solution requires you to delete the temporary file after you sent the mail. This might look like this:
MailMessage mail = new MailMessage();
List<MailMessage> mailsToAttach = mails.FindAll(m => m.Date.CompareTo(otherDate) < 0);
List<string> tempFiles = new List<string>();
foreach (var item in mailsToAttach)
{
string tempFile = mail.AttachMail(item);
tempFiles.Add(tempFile);
}
// smtp.Send(mail)
foreach (var item in tempFiles)
{
System.IO.File.Delete(item);
}
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.
This is in my new class top:
MailMessage photosmessage;
This is the method i have in my new class:
public void SendPhotos(string fileNameToSend)
{
try
{
MailAddress from = new MailAddress("chocolade#gmail.com", "User " + (char)0xD8 + " Name",
System.Text.Encoding.UTF8);
MailAddress to = new MailAddress("MyEimalOfMyInternet");
photosmessage = new MailMessage(from, to);
photosmessage.Body = "Please check the log file attachment i have some bugs.";
string someArrows = new string(new char[] { '\u2190', '\u2191', '\u2192', '\u2193' });
photosmessage.Body += Environment.NewLine + someArrows;
photosmessage.BodyEncoding = System.Text.Encoding.UTF8;
photosmessage.Subject = "Log File For Checking Bugs" + someArrows;
photosmessage.SubjectEncoding = System.Text.Encoding.UTF8;
Attachment myAttachment = new Attachment(fileNameToSend, MediaTypeNames.Application.Octet);
photosmessage.Attachments.Add(myAttachment);
SmtpClient docsend = new SmtpClient("smtp.gmail.com", 587);
docsend.SendCompleted += new SendCompletedEventHandler(docsend_SendCompleted);
docsend.EnableSsl = true;
docsend.Timeout = 10000;
docsend.DeliveryMethod = SmtpDeliveryMethod.Network;
docsend.UseDefaultCredentials = false;
docsend.Credentials = new NetworkCredential("gmailusername", "gmailpassword");
string userState = "test message1";
docsend.SendAsync(photosmessage, userState);
SendLogFile.Enabled = false;
}
catch (Exception errors)
{
Logger.Write("Error sending message :" + errors);
}
}
Im using this method in Form1 like this:
se.SendPhotos(outputtext+"\\"+"textfiles.zip");
se.SendPhotos(outputphotos + "\\" + "photofiles.zip");
Firsrt time its sending zipped file of some text files inside the zip file is about 5kb
Sending the zip file no problems.
Then its sending a zip file of 19mb that inside there are some images/photos each photos about 7.55mb
This time the zip file never get to my email.
The first zip file of the text files i get it but the second one i never get it.
Im using my gmail email account to send this files to my regular isp email account.
I know in gmail you cant send more then 25mb but the zip file of the photos is 19mb
What else could be the reason i never get the second zip file ?
Edit:
I think i know what is the problem.
When getting and creating the zip of text file i did a filter ".txt" but when doing it with the photos zip file i did ".*" all the files:
string[] photosfiles = Directory.GetFiles(s, "*.*", SearchOption.AllDirectories);
The result is i had a file with .ini in the zip file.
How can i filter for all images types ?
string[] photosfiles = Directory.GetFiles(s, "*.jpg", SearchOption.AllDirectories);
This will work for jpg files only but if i want also to get png or bmp ?
For the added question (in your Edit), you can use the following code to get all the files you want:
string[] extensions = {"*.bmp","*.jpg","*.png", "*.gif" };//add extensions you want to filter first
var filenames = extensions.SelectMany(x => Directory.GetFiles(s, x));
Hope it helps.
Try sending a simple text file to vet your solution even works. If that comes through, then your ISP may be filtering. Some ISP's have 5mb limits. Also make sure you only have pictures in those zips. If you have any exes etcs those can be blocked. Check your spam folder.
Really though, just make sure a text attachment goes through.
Do the attachments only contain photos and text files, Google will open up zip files and check to see if you're not attaching anything that could be potentially dangerous (.exe, .bat, etc.)
Or, you could be sending a zip file within of your zip files. (see here for a full list of what can't be sent over Gmail)
I would imagine in any case where you are violating a sending condition, the SMTP request would be implicitly rejected.
Is the photo zip file just photos? I tried looking online to see if Gmail does implement further restrictions on SMTP requests but couldn't find anything...