In previous versions of classic ASP and ASP.NET 1.x, there has always been a requirement that an
attachment exist on the hard disk before it can be attached to an outbound email. For example,
if you want to attach a file to an email in ASP.NET 1.x, you need to reference a location on
the disk using a file path. Makes sense right? To attach a file you must have a file.
However, in ASP.NET 2.x, you add an attachment without having a file, all you need is a Stream.
Having to reference the hard disk for an attachment was particularly annoying when you where
generating the attachment at the same time you generated the email. For example, if the email
was a message that contain a sales receipt as the attachment, and both where being generated
from the web page. The annoying part was that you were sending an email, and it would be nice
if you could "attach" the in-memory file without having to write it to disk.
In order to successfully write the attachment to disk you need to handle a couple of tricky issues:
Your ASP or ASP.NET page had to have permissions to write and read from the hard disk. This usually has to be arranged with IT and it made the complexity of setting up the web site on another server a degree more difficult.
Not only must you have permissions to write to the hard drive, you need to make sure that you write the attachment outside of the web root (and all virtual directories) to prevent errant web requests from being able to view the attachments. This usually makes IT tear their hair out worrying about security issues.
Each attachment needs a unique name so that it doesn't conflict with other attachments. This can be a dilemma since the name of the file on the hard drive is the name of the file in the email. If the name is too random, the recipient will not want to open it. If it is too structured, you risk having it conflict with another file.
You needed to delete the file from the disk after the email was sent. Otherwise the hard disk would fill up from attachment files (causing complete balding in IT). On top of that you need to make sure to delete the file, even if the sending of the file failed -- i.e. handle the error cases correctly.
Dealing with all these issues was trickier than the actual sending of the email and always caused me to sigh and wish I could just attach the memory allocated and formatted for the attachment directly to the email, naming it whatever I wanted. Some smart person at Microsoft thought of just that with ASP 2.x and now you can attach a Stream to a file. And since a MemoryStream is a Stream we can attach directly from memory.
MemoryStream
The name "Streams" defines a set of classes in CLR that are derived from the Stream class.
My favorites are FileStream and MemoryStream. The Stream class defines how subclasses should behave
-- the stream class itself is an abstraction for a sequence of bytes. Streams can be manipulated
in many ways, however using a TextWriter or BinaryWriter makes it much easier. A MemoryStream is
just an allocation of memory to hold your data sequence. It contains a position pointer and Reader
and Writer classes can add bytes or read bytes from the position. Writing to a MemoryStream is no
different then writing to a FileStream. Here is an example of writing a file to the hard disk and
attaching it to email using C# in .NET 1.1
using System.Web.Mail;
String filename = Server.MapPath("/test.txt");
FileStream filestream = new FileStream(filename, FileMode, FileAccess);
StreamWriter streamwriter = new StreamWriter(file);
streamwriter.Write("Hello world!");
streamwriter.Close();
filestream.Close();
msgMail.BodyFormat = MailFormat.Text;
msgMail.Body = "Check out the attachment!";
msgMail.Attachments.Add(new MailAttachment(filename));
SmtpMail.Send(msgMail);
Notice the threading issue. If we are reusing test.txt and there are many requests, then the file might be overridden by one use before it is sent by a previous user. There might also be file access issues since the file is being read and written to at the same time.
We can solve this problem in .NET 1.1 by changing the file name, the easiest way is to use a GUID:
try
{
// ... code to create attachment and send email.
}
finally
{
if (File.Exists(filename))
File.Delete(filename));
}
We need to execute the delete inside the finally since we always want to clean-up the
file even if there is an exception. However, now that we have added the
try we need to add our own error handling.
.NET 2.0
.NET 2.0 comes to the rescue by allowing us to attach any class derived from the Stream class -- including Memory Stream. For example:
using System.Net.Mail;
MemoryStream memorystream = new FileStream();
StreamWriter streamwriter = new StreamWriter(file);
streamwriter.Write("Hello world!");
streamwriter.Close();
Notice that we don't have a filename, this is because the stream exists only in RAM. However, we still use the StreamWriter, just like we did when we used the FileStream object.
Now we use the whole new object structure for .NET 2.0 located in the namespace System.Net.Mail:
MailMessage Message = new MailMessage();
Message.Subject = "Attachment Test";
Message.Body = "Check out the attachment!";
Message.To.Add("webmaster@15Seconds.com");
Message.From = "someone@somedomain.com";
Notice that we created the attachment from the MemoryStream and we got to name the attachment anything we want. The name of the attachment in the second parameter is the name of the file in the email, not the name on the local system hard drive. In fact the attachment never goes to the local hard drive. The third parameter is the Mime type of the attachment, in our case this is text.
Summary
The RAM that is used for the Memory Stream gets released when the garbage collector cleans up
the Memory Stream object. In the case of ASP.NET, that is sometime after the response is finished.
The garbage collector for .NET is much more efficient way of "cleaning up" the attachments
then writing our own code to delete files from the hard drive.
Because we are not using the hard drive with the memory stream, we don't need special permissions
to create files on the hard drive. IT doesn't need to configure the permissions of the .aspx page,
nor the NTFS ACLs.
Because we are not using a fixed file path, there are no threading conflicts and we can send
the file with any file name we want.
RAM is also much faster then disk IO, so the attachment will generate faster and be sent
faster then if we had to write to disk.
The code to implement and the considerations needed to implement are greatly reduced,
making a memory stream attachment a more secure, faster, and easier solution.
About the Author
Wayne Walter Berry founded Sign Me Up Marketing in 1996. As with all start-up companies, Berry is responsible for the design, product development, and strategy that drives this innovative high-tech company.
As a former Microsoft design engineer, and the director of development for FreeShop, Berry's expertise includes software design, development, marketing and online business. The web site he created as a hobby to assist in distributing information to Active Server Page developers became his first product, 15 Seconds. The recent sale of 15 Seconds to Internet.com allowed Berry time to develop his latest product, XBuilder, and Kulshan.com, a community web-site for his hometown, Bellingham, Washington.
The Bellingham native graduated from Bellingham High School, and Western Washington University with a BS in computer science. Berry is the author of three books, ActiveX Unleashed Programming, Windows Registry Guide, and Special Edition - Using Microsoft Internet Information Server 4.0. A popular and respected speaker, Berry recently spoke to the Professional ASP Developers Conference in Denver and has been invited to speak to an international ASP conference in Sweden.
While the .NET Framework made building ASP.NET applications easier then it had ever been in the past, .NET 2.0 builds on that foundation in order to take things to the next level. This article shows you to how to construct an N-Tier ASP.NET 2.0 Web application by leveraging the new features of ASP.NET 2.0 and SQL Server 2005.
[Read This Article][Top]
With the release of ASP.NET 2.0, Microsoft has greatly increased the power of ASP.NET by introducing a suite of new features and functionalities. As part of this release, ASP.NET 2.0 also comes with a host of new special files and folders that are meant to be used to implement a specific functionality. This article examines these new files and folders in detail and provides examples that demonstrate how to utilize them to create ASP.NET 2.0 applications.
[Read This Article][Top]
Alex Homer continues his detailed look at the major changes to the DataSet class. In this part, he looks at two features that allow developers to work with data in a more structured and efficient way when using the DataSet with a SQL Server 2005 database server.
[Read This Article][Top]
Alex Homer continues his detailed look at the major changes to the DataSet class. In this part, he looks at two features that allow developers to work with data in a more structured and efficient way when using the DataSet with a SQL Server 2005 database server. [Read This Article][Top]
In this article, Alex Homer looks at the changes between the version 1.x and version 2.0 DataSet and their associated classes, showing you how you can take advantage of the new features to improve your applications' capabilities and performance. [Read This Article][Top]
In this article, Alex Homer looks at the changes between the version 1.x and version 2.0 DataSet and their associated classes, showing you how you can take advantage of the new features to improve your applications' capabilities and performance. [Read This Article][Top]
In ASP.NET 2.0 and Visual Studio 2005, you can quickly program custom authentication pages with the provided Membership Login controls. In this article, Dina Fleet Berry examines the steps involved in using the Login control with a custom SQL Server membership database.
[Read This Article][Top]
In this article, Thiru Thangarathinam examines .NET 2.0's new ClickOnce deployment technology that is designed to ease deployment of Windows forms applications. This new technology not only provides an easy application installation mechanism, it also eases deployment of upgrades to existing applications. [Read This Article][Top]
With ASP.NET 2.0, Microsoft has made great strides in increasing developer productivity and has made implementing previously complex solutions relatively easy. Where this version of ASP.NET really shines, however, is in its new administrative tools that allow developers to spend less time managing the configuration of the servers and software and more time developing great code.
[Read This Article][Top]
Thiru Thangarathinam introduces ASP.NET 2.0's new TreeView control which provides a seamless way to consume and display information from hierarchical data sources. The article discusses this new control in depth and explains how to use this feature rich control in your ASP.NET applications. [Read This Article][Top]
Mailing List
Want to receive email when the next article is published? Just Click Here to sign up.