In this article I will explain how to attach multiple files and send email like GMAIL in ASP.Net using jQuery Uploadify Plugin.
HTML Markup
Below is the HTML Markup which is nothing but a simple form to send email.
<table>
<tr><td>To:</td><td><asp:TextBox ID="txtTo" runat="server"></asp:TextBox></td></tr>
<tr><td>Subject:</td><td><asp:TextBox ID="txtSubject" runat="server"></asp:TextBox></td></tr>
<tr><td>Body:</td><td><asp:TextBox ID="txtBody" runat="server" TextMode="MultiLine"></asp:TextBox></td></tr>
<tr><td></td><td><asp:FileUpload ID="FileUpload1" runat="server"/></td></tr>
<tr><td></td><td><table id="attachedfiles"></table></td></tr>
<tr><td></td><td><asp:Button ID="btnSend" runat="server" Text="Send" OnClick="btnSend_Click"/></td></tr>
</table>
Generating the Unique Key for Cache
In the Page Load event of the Page a Unique Key for Cache is generated and is stored in a protected ViewState property
C#
protected string Key
{
get { return ViewState["Key"].ToString(); }
set { ViewState["Key"] = value; }
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
this.Key = Guid.NewGuid().ToString();
Cache[this.Key] = new List<HttpPostedFile>();
}
}
VB.Net
Protected Property Key() As String
Get
Return ViewState("Key").ToString()
End Get
Set(value As String)
ViewState("Key") = value
End Set
End Property
Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
If Not IsPostBack Then
Me.Key = Guid.NewGuid().ToString()
Cache(Me.Key) = New List(Of HttpPostedFile)()
End If
End Sub
Initially this article was using Session variable, but due to some reason Uploadify does not retain Session in Firefox browser hence I had to change the functionality to make use of Cache instead. But since Cache is not user specific i.e. shared across all users, we will need to generate a unique GUID Key for each user so that the Files can be uniquely identified.
Uploading multiple files as email attachments
You will notice that I have used <% %> server side Tags <%=Key %> in the script, this is the ViewState protected Property (discussed earlier) which is used to pass the Cache Unique key to access the Cache variable during Upload and also during file removal.
<link rel="Stylesheet" type="text/css" href="CSS/uploadify.css" />
<script type="text/javascript" src="scripts/jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="scripts/jquery.uploadify.js"></script>
<script type="text/javascript">
$(function () {
$("[id*=FileUpload1]").fileUpload({
'uploader': 'scripts/uploader.swf',
'cancelImg': 'images/cancel.png',
'buttonText': 'Attach Files',
'script': 'UploadCS.ashx',
'folder': 'uploads',
'multi': true,
'auto': true,
'scriptData': {key:'<%=Key %>'},
'onSelect': function (event, ID, file) {
$("#attachedfiles tr").each(function () {
if ($("td", this).eq(0).html() == file.name) {
alert(file.name + " already uploaded.");
$("[id*=FileUpload1]").fileUploadCancel(ID);
return;
}
});
},
'onComplete': function (event, ID, file, response, data) {
$("#attachedfiles").append("<tr><td>" + file.name + "</td><td><a href = 'javascript:;'>[x]</a></td></tr>");
}
});
});
</script>
The files are uploaded via Generic Handler Upload.ashx. I am storing the uploaded files in Cache. Also the uploaded files are dynamically displayed on the page.
The Cache Unique Key passed to the Handler via Uploadify Plugin is
C#
<%@ WebHandler Language="C#" Class="UploadCS" %>
using System;
using System.Web;
using System.IO;
using System.Collections.Generic;
public class UploadCS : IHttpHandler {
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "text/plain";
context.Response.Expires = -1;
try
{
List<HttpPostedFile> files = (List<HttpPostedFile>)context.Cache[context.Request.QueryString["key"]];
HttpPostedFile postedFile = context.Request.Files["Filedata"];
files.Add(postedFile);
string filename = postedFile.FileName;
context.Response.Write(filename);
context.Response.StatusCode = 200;
}
catch (Exception ex)
{
context.Response.Write("Error: " + ex.Message);
}
}
public bool IsReusable {
get {
return false;
}
}
}
VB.Net
<%@ WebHandler Language="VB" Class="UploadVB" %>
Imports System
Imports System.Web
Imports System.IO
Imports System.Collections.Generic
Public Class UploadVB : Implements IHttpHandler
Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
Try
Dim files As List(Of HttpPostedFile) = DirectCast(context.Cache(context.Request.QueryString("key")), List(Of HttpPostedFile))
Dim postedFile As HttpPostedFile = context.Request.Files("Filedata")
files.Add(postedFile)
Dim filename As String = postedFile.FileName
context.Response.Write(filename)
context.Response.StatusCode = 200
Catch ex As Exception
context.Response.Write("Error: " + ex.Message)
End Try
End Sub
Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class
Removing Attached files using jQuery AJAX
I have also added functionality to remove files like we have in GMAIL browser. To achieve this I have made use of jQuery AJAX and ASP.Net Page Methods
<script type="text/javascript">
$("#attachedfiles a").live("click", function () {
var row = $(this).closest("tr");
var fileName = $("td", row).eq(0).html();
$.ajax({
type: "POST",
url: "CS.aspx/RemoveFile",
data: '{fileName: "' + fileName + '", key: "<%=Key %>" }',
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function () { },
failure: function (response) {
alert(response.d);
}
});
row.remove();
});
</script>
The above JavaScript function makes call to a WebMethod defined below which deletes the file from the Cache variable where it was stored.
C#
[WebMethod]
public static void RemoveFile(string fileName, string key)
{
List<HttpPostedFile> files = (List<HttpPostedFile>)HttpContext.Current.Cache[key];
files.RemoveAll(f => f.FileName.ToLower().EndsWith(fileName.ToLower()));
}
VB.Net
<WebMethod()> _
Public Shared Sub RemoveFile(fileName As String, key As String)
Dim files As List(Of HttpPostedFile) = DirectCast(HttpContext.Current.Cache(key), List(Of HttpPostedFile))
files.RemoveAll(Function(f) f.FileName.ToLower().EndsWith(fileName.ToLower()))
End Sub
Sending the email with multiple attachment
Finally here’s the code to send email using GMAIL account.
C#
protected void btnSend_Click(object sender, EventArgs e)
{
using (MailMessage mailMessage = new MailMessage())
{
mailMessage.From = new MailAddress("user@gmail.com");
mailMessage.Subject = txtSubject.Text.Trim();
mailMessage.Body = txtBody.Text.Trim();
mailMessage.IsBodyHtml = true;
mailMessage.To.Add(new MailAddress(txtTo.Text.Trim()));
List<HttpPostedFile> files = (List<HttpPostedFile>)Cache[this.Key];
foreach (HttpPostedFile file in files)
{
mailMessage.Attachments.Add(new Attachment(file.InputStream, Path.GetFileName(file.FileName), file.ContentType));
}
SmtpClient smtp = new SmtpClient();
smtp.Host = "smtp.gmail.com";
smtp.EnableSsl = true;
System.Net.NetworkCredential NetworkCred = new System.Net.NetworkCredential();
NetworkCred.UserName = mailMessage.From.Address;
NetworkCred.Password = "<Password>";
smtp.UseDefaultCredentials = true;
smtp.Credentials = NetworkCred;
smtp.Port = 587;
smtp.Send(mailMessage);
}
Cache.Remove(this.Key);
Response.Redirect(Request.Url.AbsoluteUri);
}
VB.Net
Protected Sub btnSend_Click(sender As Object, e As EventArgs)
Using mailMessage As New MailMessage()
mailMessage.From = New MailAddress("user@gmail.com")
mailMessage.Subject = txtSubject.Text.Trim()
mailMessage.Body = txtBody.Text.Trim()
mailMessage.IsBodyHtml = True
mailMessage.[To].Add(New MailAddress(txtTo.Text.Trim()))
Dim files As List(Of HttpPostedFile) = DirectCast(Cache(Me.Key), List(Of HttpPostedFile))
For Each file As HttpPostedFile In files
mailMessage.Attachments.Add(New Attachment(file.InputStream, Path.GetFileName(file.FileName), file.ContentType))
Next
Dim smtp As New SmtpClient()
smtp.Host = "smtp.gmail.com"
smtp.EnableSsl = True
Dim NetworkCred As New System.Net.NetworkCredential()
NetworkCred.UserName = mailMessage.From.Address
NetworkCred.Password = "<Password>"
smtp.UseDefaultCredentials = True
smtp.Credentials = NetworkCred
smtp.Port = 587
smtp.Send(mailMessage)
End Using
Cache.Remove(Me.Key)
Response.Redirect(Request.Url.AbsoluteUri)
End Sub
Downloads