Introduction
It is difficult to find a decent upload control that handles large files, shows a progress bar, and still works in a medium-trust hosting environment. The problem with these controls is that medium-trust does not allow Reflection. But this does not mean that we are out of luck. One of the problems these controls strive to overcome is the server running out of memory when large files are uploaded by streaming the uploaded file to disk. But the HttpPostedFile class description states that "By default, all requests, including form fields and uploaded files, larger than 256 KB are buffered to disk, rather than held in server memory." So, we don't have to worry about this problem. The other issue is feedback to the user by way of progress bar. Enter Flash's fileReference API, which allows you to upload a file from Flash, and also keep track of it. FileReferenceList allows the user to select multiple files.
Using the Code
I have finally gotten around to updating the flash component of this article. I have re-written it using Flex, and have tried to add some of the suggestions from the message board. Some of the new features include adding files without clearing previously added files, removing individual files, individual progress bar as well as total progress bar, cancelling and then uploading does not re-upload previously uploaded files, ability to add and remove files while uploading files. The code is written using code behind and should be easier to modify. Moreover, all you need to make changes is a text editor and then you can compile it using the Flex 2 SDK, which is a free download from Adobe. Error handling should be better as well, though additional code may need to be added to handle it better. The only change to the article is the updated Flash component, everything else works the same. I have left the old fileUpload.swf and fileUpload.fla file in the source code in case anyone still wanted it. The source for the new FlashFileUpload.swf is contained in the FlashFileUpload_src.zip file and contains four files:
- FlashFileUpload.mxml - Main application GUI
- components\ApplicationClass.as - Main application code behind
- components\FileUpload.as -
FileUpload class used for individual file uploading
- components\FileUploadEvent.as - A custom event class
- components\FileUploadProgressChangedEvent.as - A custom event class
I won't put the file's code in the article, you can download it and read the comments in the code. Also, a small declaration: I have little ActionScript experience, only from the first upload control and this one. Neither do I have any Flex experience. This is the first time I have looked at Flex. So if I did something in the code that is not best practice, or if it could have been done better another way, it is due to my inexperience with Flex and ActionScript and the small amount of time I put into writing it. I give no guarantees for this control. I leave the rest of the article as is except the .swf file and height and width of the flash control in Default.aspx. I hope you find this article helpful.
The code isn't very complicated, and is pretty well commented. The code includes:
- Default.aspx
- Default.aspx.cs
- Upload.cs
- Web.config
Default.aspx
Collapse
<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Flash Upload</title>
</head>
<body>
<form id="form1" runat="server"
enctype="multipart/form-data" method="POST">
<div>
<%--
Adding the flash uploader to the page.
<param name="wmode" value="transparent"> makes
the movie transparent so any page styling can shine through.
FlashVars is set to pass in the upload page
and a JavaScript function if desired.
The JavaScript function fires when
the upload is completed. This allows us to
call a codebehind function to refresh a gridView or anything else.
A link button is used to perform this function.
The JavaScript is added in the pageLoad in the code behind.
To edit the flash file, Adobe/Macromedia
has a 30-day trial of Macromedia Flash
for download. After that, you're on your own.
--%>
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
codebase="http://fpdownload.macromedia.com/pub/shockwave/
cabs/flash/swflash.cab#version=9,0,0,0"
width="575" height="375" id="fileUpload" align="center">
<param name="allowScriptAccess" value="sameDomain" />
<param name="movie" value="FlashFileUpload.swf" />
<param name="quality" value="high" />
<param name="wmode" value="transparent">
<PARAM NAME=FlashVars
VALUE='uploadPage=Upload.axd<%=GetFlashVars()%>
&completeFunction=UploadComplete()'>
<embed src="FlashFileUpload.swf"
FlashVars='uploadPage=Upload.axd<%=GetFlashVars()%>
&completeFunction=UploadComplete()'
quality="high" wmode="transparent" width="575" height="375"
name="fileUpload" align="middle" allowScriptAccess="sameDomain"
type="application/x-shockwave-flash"
pluginspage="http://www.macromedia.com/go/getflashplayer" />
</object>
<%--This link button is here so we can call
the LinkButton1_Click even from JavaScript.
Make the text empty so the link doesn't
show up on the page.--%>
<asp:LinkButton ID="LinkButton1" runat="server"
OnClick="LinkButton1_Click"></asp:LinkButton>
</div>
</form>
</body>
</html>
Default.aspx.cs
Collapse
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string jscript = "function UploadComplete(){";
jscript += string.Format("__doPostBack('{0}','');",
LinkButton1.ClientID.Replace("_", "$"));
jscript += "};";
Page.ClientScript.RegisterClientScriptBlock(this.GetType(),
"FileCompleteUpload", jscript, true);
}
protected string GetFlashVars()
{
return "?" + Server.UrlEncode(Request.QueryString.ToString());
}
protected void LinkButton1_Click(object sender, EventArgs e)
{
}
}
Upload.cs
Collapse
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.IO;
public class Upload : IHttpHandler
{
public Upload()
{
}
#region IHttpHandler Members
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
if (context.Request.Files.Count > 0)
{
string tempFile = context.Request.PhysicalApplicationPath;
for(int j = 0; j < context.Request.Files.Count; j++)
{
HttpPostedFile uploadFile = context.Request.Files[j];
if (uploadFile.ContentLength > 0)
{
uploadFile.SaveAs(string.Format("{0}{1}{2}",
tempFile,"Upload\\", uploadFile.FileName));
// HttpPostedFile has an InputStream
// also. You can pass this to
// a function, or business logic.
// You can save it a database:
//byte[] fileData = new byte[uploadFile.ContentLength];
//uploadFile.InputStream.Write(fileData, 0,
// fileData.Length);
// save byte array into database.
// something I do is extract files
// from a zip file by passing
// the inputStream to a function
// that uses SharpZipLib found here:
// http://www.icsharpcode.net/OpenSource/SharpZipLib/
// and then save the files to disk.
}
}
}
// Used as a fix for a bug in mac flash player that makes the
// onComplete event not fire
HttpContext.Current.Response.Write(" ");
}
#endregion
}
Web.config
="1.0"
<configuration>
<appSettings/>
<connectionStrings/>
<system.web>
<compilation debug="true" />
<authentication mode="Windows" />
<customErrors mode="Off" />
<httpHandlers>
<remove verb="POST,GET" path="Upload.axd"/>
<add verb="POST,GET" path="Upload.axd" type="Upload"/>
</httpHandlers>
<httpRuntime maxRequestLength="1550000"/>
</system.web>
</configuration>
Points of Interest
One quick note. To get the page to refresh any data, such as a GridView of the uploaded files, I create a LinkButton with an OnClick event. Then, delete any text for the LinkButton so it doesn't show up in the browser. Then, when the download is complete, I call a JavaScript function that calls the __doPostBack function which is ASP.NET's way of doing a button click event. The code for this is in the page_load method. In the example, the page refresh is set up. To see it without the refresh, just comment out the __doPostBack in the page_load.