ASPNet EMail attachment from SQL BLOB - asp.net

We are storing scheduled email information in SQL Server 2005. Included in the email information are 0 - 3 attachments of varying filetype. I have a VB.net Windows service that runs 'x' number of minutes, populates a dataset with the email info from SQL Server, builds the emails, and sends them via ASPNet EMail.
Trying to build and add an attachment from a BLOB causes the email send method to time out, even if the attachment is just a 15 byte text file. Hardcoding the path directly to the file works fine. I've tried several methods and can't get it right.

Hmm, I've done a similar thing. For each of my attachments I store the data as a binary column, and a separate column containing the size of the file in bytes. To obtained from the database I wrote something like (usingSqlCommand and SqlDataReader classes):
attachment = new byte[size];
sqlReader.GetBytes(sqlReader.GetOrdinal("attachment"), 0, attachment, 0, size);
System.IO.MemoryStream s = new System.IO.MemoryStream(attachment, true);
s.Write(attachment, 0, attachment.Length);
And too attached to a MailMessage class (mm for short);
System.IO.MemoryStream s = new System.IO.MemoryStream(attachment, false);
Attachment attached = new Attachment(s, "some file name");
mm.Attachments.Add(attached);
Maybe these extracts will help! I've pulled the code from two different custom classes, so it's a little clunky with the MemoryStream object in each extract.

Related

The process cannot access the file because it is being used by another process at SharePoint 2010 C# code

I am creating custom timer job service in SharePoint 2010 using asp.net 3.5 and c#.In this service, business logic is that i have to create zip file containing list of applications as excel report for each client.for this, i am using Ionic.zip third party dll and ZipFile class for creating zip file and storing this zip file on hard disk having some path.here scenario is that my code contains two foreach loops, upper for list of clients and inner for list of applications.each client may have no. of applications.I am adding these applications to zip file, storing it on hard disk and attaching this file to mail for sending to clients, but my problem is that I am trying to delete zip file before gone to next client, so that there should not be any files on hard disk, but I am getting error as "The process cannot access the file because it is being used by another process".also I have tried to attach output stream for excel report as mail attachment but I am getting zero bytes in attachment. how should i overcome this error.
I am giving simple code below
foreach(list of clients)////may have no. of clients
{
string zipFileDownloadPath = String.Empty;
foreach(list of applications)//may have no. of applications
{
HttpWebResponse resp = (HttpWebResponse)httpReq.GetResponse();
Stream excelReport = resp.GetResponseStream();
zipFile.AddEntry(appName, excelReport);
}
zipFileDownloadPath = clientFolder + #"\" + client["client_name"] + "_" + reportDate + ".zip";
zipFile.Save(zipFileDownloadPath);
mail.Attachments.Add(new Attachment(zipFileDownloadPath));
smtp.Send(mail);//mail have body, subject etc.
//here I am deleting files
if (Directory.Exists(clientFolder))
{
Directory.Delete(clientFolder, true);//here I am getting error
}
}
I the above code I have also tried so save zipfile to output stream so that there should not be any need for storing files on hard disk and attach this stream to mail attachment, but problem is that, i am getting proper bytes in output stream but when mail is sent, i am getting zero byes in attachment.
//here is code for attaching output stream to mail
foreach(list of clients)////may have no. of clients
{
foreach(list of applications)//may have no. of applications
{
HttpWebResponse resp = (HttpWebResponse)httpReq.GetResponse();
Stream excelReport = resp.GetResponseStream();
zipFile.AddEntry(appName, excelReport);
}
Stream outputStream = new MemoryStream();
zipFile.Save(outputStream);
mail.Attachments.Add(new Attachment(outputStream,"ZipFileName" MediaTypeNames.Application.Zip);));
smtp.Send(mail);//mail have body, subject etc.
}
Try moving the position of the stream to it's begiining before sending it to the attachement:
outputStream .Seek(0, SeekOrigin.Begin);
Also before deleting your file make sure you dispose the zipFile object:
zipFile.Dispose()
Or alternately (better) wrap it in a using statement.
Also unless I am missing something if you are using streams, why do you need to save the files to the harddrive? just use the streams, something along the lines of:
var ms = new new MemoryStream();
zipFile.Save(ms);
ms.Seek(0, SeekOrigin.Begin);
mail.Attachments.Add(new Attachment(ms,"ZipFileName" MediaTypeNames.Application.Zip));
zipFile.Dispose()
Special thanks to Luis.Luis has solved my problem.
Hi Everyone Finally I have solved my problem. problem was that I was saving the zip file on output stream so stream was reading exact bytes and reaching at it's last position and I was attaching same stream to attachment that's why i was getting zero bytes in mail attachment.so solution for this is that seek the position of output stream to begin after saving to zip file and before attaching to it to mail. please refer following code for reference.
Stream outputStream = new MemoryStream();
zipFile.Save(outputStream);
outputStream .Seek(0, SeekOrigin.Begin);
mail.Attachments.Add(new Attachment(outputStream,"ZipFileName" MediaTypeNames.Application.Zip);));

Save File Prompt instead of FileWriteAllBytes

Long time lurker first time poster. Working with .Net / Linq for just a few years so I'm sure I'm missing something here. After countless hours of research I need help.
I based my code on a suggestion from https:http://damieng.com/blog/2010/01/11/linq-to-sql-tips-and-tricks-3
The following code currently saves a chosen file (pdf, doc, png, etc) which is stored in an sql database to the C:\temp. Works great. I want to take it one step further. Instead of saving it automatically to the c:\temp can I have the browser prompt so they can save it to their desired location.
{
var getFile = new myDataClass();
//retrieve attachment id from selected row
int attachmentId = Convert.ToInt32((this.gvAttachments.SelectedRow.Cells[1].Text));
//retrieve attachment information from dataclass (sql attachment table)
var results = from file in getFile.AttachmentsContents
where file.Attachment_Id == attachmentId
select file;
string writePath = #"c:\temp";
var myFile = results.First();
File.WriteAllBytes(Path.Combine(writePath, myFile.attach_Name), myFile.attach_Data.ToArray());
}
So instead of using File.WriteAllBytes can I instead take the data returned from my linq Query (myFile) and pass it into something that would prompt for the user to save the file instead?). Can this returned object be used with response.transmitfile? Thanks so much.
Just use the BinaryWrite(myFile.attach_Data.ToArray()) method to send the data since it is already in memory.
But first set headers appropriately, for example:
"Content-Disposition", "attachment; filename="+myFile.attach_Name
"Content-Type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
Content-type guides the receiving system on how it should handle the file. Here are more MS Office content types. If they are known at the point the data is stored, the content-type should be stored, too.
Also, since the file content is the only data you want in the response, call Clear before and End after BinaryWrite.

Can I test the validity of an image file before uploading it in ASP.NET?

I have an ASP.NET web application that allows the user to upload a file from his PC to a SQL Server database (which is later used to generate an image for an tag). Is there an "easy" way to test the image within .NET to validate that it does not contain anything malicious before saving it?
Right now, I use this:
MemoryStream F = new MemoryStream();
Bitmap TestBitmap = new Bitmap(Filename);
TestBitmap.Save(F, System.Drawing.Imaging.ImageFormat.Png);
int PhotoSize = (int)F.Length;
Photo = new byte[PhotoSize];
F.Seek(0, SeekOrigin.Begin);
int BytesRead = F.Read(Photo, 0, PhotoSize);
F.Close();
Creating TestBitmap fails if it is not an image (e.g. if Filename is the name of a text file), but apparently this doesn't stop a file that is an image with malicious code appended to it from loading as an image, so saving it as a MemoryStream and then writing the stream to a byte array (which is later saved in the database) supposedly fixes this.
To avoid people pass programs and other information's using the ability to upload photos to your site you can do two main steps.
Read and save again the image with your code to remove anything elst.
Limit the size of each image to a logical number.
To avoid some one upload bad code and run it on your server you keep an isolate folder with out permission to run anything. More information's about that on:
I've been hacked. Evil aspx file uploaded called AspxSpy. They're still trying. Help me trap them‼
And a general topic on the same subject: Preparing an ASP.Net website for penetration testing

display resume to user in txt format

I have Stored my resume(word document) in my database.and i just want to show that resume in txt format to user. how can i show it to user?
I'm not that good in ASP.NET, but check the links below. Maybe it's not a solution, you want, but can be useful.
Save-Read-Image-Database
There isn't that much of difference, depending on what type to you use for your resume, a varchar or maybe varbinary.
How to Create a text file in ASP .NET
Reading and Writing Text Files with the .NET Framework
Tutorials on how to read/write a file in asp.net.
Good Luck!
UPDATE:
So if it's a varbinary type, you can read as it was shown in first link. I'll paste a part with some changes.
connection.Open();
SqlCommand command1 = new SqlCommand("select <resume_content> from <resume_table> where id = #id", connection);
SqlParameter myparam = command1.Parameters.Add("#id", SqlDbType.Int);
myparam.Value = <your_value>
byte[] resume = (byte[])command1.ExecuteScalar();
MemoryStream str = new MemoryStream();
str.Write(resume, 0, resume.Length);
After that, just save the Stream to a file. Here's the link
Save a Stream to a File
UPDATE 2:
It's because you SELECT two columns. It takes FileName as the value for byte[]. For two columns you'd need to use ExecuteReader instead of ExecuteScalar.
Check this link
http://www.developerfusion.com/article/4278/using-adonet-with-sql-server/2/
Probably best to direct the user to download the document directly, and they can open it themselves.

Merging/filling pdf form file with xml data

Let's say I have a pdf form file available at website which is filled by the users and submitted to the server. On the server side (Asp.Net) I would like to merge the data that I receive in xml format with the empty pdf form that was filled and save it.
As I have found there are several possible ways of doing it:
Using pdf form created by adobe acrobat and filling it with itextsharp.
Using pdf form created by adobe acrobat and filling it with FDF Toolkit .net (which seems to be using itextsharp internally)
Usd pdfkt to fill the form.
Use pdf form file created with adobe livecycle and merge the data by using Form Data Integration Service
As I have no experience with this kind of task can you advise which option would be better/easier and give some additional tips?
Thank you in advance.
I would suggest using the 4th approach if possible because it would be cleaner. You would be using solutions specifically tailored for what you are asking to do, but if you don't have the available resources for such a solution I would suggest using the 1st option.
The 1st option is what I have recently dove into. I have found it relatively painless to implement.
Option 1 is possible if the following applies:
You have control of development of PDF forms.
You have control of formating xml data
You have can live with having uncompressed (fastweb=false) PDF files
Example of implementation:
Using Adobe Acrobat to generate a PDF form. Tip: Use Adobe Native Fonts when generating the forms. For each control you add that is not a native font it will import the font used and bloat the file when it is not compressed, and to my knowledge ITextSharp currently does not produce compressed PDFs.
Using ITextSharp Library to combine XML data with the PDF form to generate a populated document. Tip: to manually populate a PDF form from xml you must map xml values to control names in the PDF form and match them by page as shown in the example below.
using (MemoryStream stream = GeneratePDF(m_FormsPath, oXmlData))
{
byte[] bytes = stream.ToArray();
Response.ContentType = "application/pdf";
Response.BinaryWrite(bytes);
Response.End();
}
// <summary>
// This method combines pdf forms with xml data
// </summary>
// <param name="m_FormName">pdf form file path</param>
// <param name="oData">xml dataset</param>
// <returns>memory stream containing the pdf data</returns>
private MemoryStream GeneratePDF(string m_FormName, XmlDocument oData)
{
PdfReader pdfTemplate;
PdfStamper stamper;
PdfReader tempPDF;
Document doc;
MemoryStream msTemp;
PdfWriter pCopy;
MemoryStream msOutput = new MemoryStream();
pdfTemplate = new PdfReader(m_FormName);
doc = new Document();
pCopy = new PdfCopy(doc, msOutput);
pCopy.AddViewerPreference(PdfName.PICKTRAYBYPDFSIZE, new PdfBoolean(true));
pCopy.AddViewerPreference(PdfName.PRINTSCALING, PdfName.NONE);
doc.Open();
for (int i = 1; i < pdfTemplate.NumberOfPages + 1; i++)
{
msTemp = new MemoryStream();
pdfTemplate = new PdfReader(m_FormName);
stamper = new PdfStamper(pdfTemplate, msTemp);
// map xml values to pdf form controls (element name = control name)
foreach (XmlElement oElem in oData.SelectNodes("/form/page" + i + "/*"))
{
stamper.AcroFields.SetField(oElem.Name, oElem.InnerText);
}
stamper.FormFlattening = true;
stamper.Close();
tempPDF = new PdfReader(msTemp.ToArray());
((PdfCopy)pCopy).AddPage(pCopy.GetImportedPage(tempPDF, i));
pCopy.FreeReader(tempPDF);
}
doc.Close();
return msOutput;
}
Save the File or post the file to the response of your ASP.Net page
Since you tagged this 'LiveCycle', I take it you have an installation of Adobe LiveCycle running somewhere (optionally, can install it somewhere).
In that case, I'd go for number 4 (with the modification of using the Adobe LiveCycle Forms ES module). The other three will undoubtedly yield compatibility issues in the long run. With the LiveCycle server (running the Forms module), you'll be able to handle any PDF, whether it's old, new, static, dynamic, compressed, Acrobat-based or LiveCycle-based.
You should be able to set things up, have the form send its data to the LiveCycle server, and use that data to populate the form. The fill can then be stored in the server's database, or routed into the PDF form (or any other form) and streamed back to the client.
Create the form using LiveCycle Designer.
The quick-and-dirty-option would be the following: Set the form to http-post (as for example an xfdf, see Acrobat for more info) to your ASP-server and publish it on the server (make sure your users don't download the form before opening it, otherwise this won't work. The form has to be opened in the web browser). Then simply capture the submissions as you would capture a http-post from a web page. Optionally, save the fill to a database. Then send the captured xfdf stream fill back to the client (could also be invoked at a later stage via a http-link). The xfdf stream will contain the URL of the form used to fill it out. The client web browser will ask the Acrobat/Adobe reader plug to handle the xfdf stream, and the plug will locate, download and populate the form pointed to by the xfdf.
The user should now be able to save the form AND it's fill - no Reader Extension needed!
You can also use iTextSharp to fill xml data into a Reader Extension enabled form. There are two things you need to set correctly:
Set PdfReader.unethicalreading = true to prevent BadPasswordException.
Set append mode in PdfStamper's constructor, otherwise the Adobe Reader Extensions signature becomes broken and Adobe Reader will display following message: "This document contained certain rights to enable special features in Adobe Reader. The document has been changed since it was created and these rights are no longer valid. Please contact the author for the original version of this document."
So all you need to do is this:
PdfReader.unethicalreading = true;
using (var pdfReader = new PdfReader("form.pdf"))
{
using (var outputStream = new FileStream("filled.pdf", FileMode.Create, FileAccess.Write))
{
using (var stamper = new iTextSharp.text.pdf.PdfStamper(pdfReader, outputStream, '\0', true))
{
stamper.AcroFields.Xfa.FillXfaForm("data.xml");
}
}
}
See How to fill XFA form using iText?

Resources