File upload component for Silverlight and ASP.NET

Posted at Fri, 07 Mar 2008 19:28:59 GMT by Wilco Bauwer

A little less than a year ago I wrote about an AsyncFileUpload component for Silverlight/ASP.NET. Several things have happened since then, so I decided to rewrite it and address some of the limitations it had along the way. To make sure we’re on the same page, let me outline the goals of this proof-of-concept:

  1. Demonstrate how OpenFileDialog and HttpWebRequest can be used to upload (large) files.
  2. Show how isolated storage can be used to enable file resumes (even across browsers on the same machine).
  3. Share some experimental object model to simplify ASP.NET/Silverlight component development.

The only real non-goal is building a file upload component of production-quality.

The result

There are a few different demos you can take a look at:

  1. AsyncFileUpload with the default template (pure HTML visualization);
  2. AsyncFileUpload with a custom template (includes two Silverlight visualizers);
  3. AsyncFileUpload used from JavaScript. Although this demo still uses ASP.NET, you could easily do the same thing from other languages/frameworks/web-servers as well.

You can also download the source code for the actual library behind all of this.

Uploading large files

Silverlight 2’s HttpWebRequest buffers the request data in memory entirely, because the underlying API it uses (provided by the browser) doesn’t support streaming. This means that – at least at the time of writing – you can’t just upload large files.

We can work around this by uploading files in chunks. It does mean we need some special handling on the server, but a basic implementation requires little effort. We just need to make sure we send some meta-data with each chunk, such that the server knows where a chunk is supposed to go. There is some overhead involved here, but we can make up for this by showing accurate and visual feedback.

By sending chunks to the server, we get support for resumes almost for free. All we need to do is keep track of some information, such as something that identifies a file and the location of the next chunk we need to send. When we store this information in isolated storage, we can use it from every browser on the user’s machine. This means you can resume uploading a file in any of the installed browsers, even if you uploaded part of the file in a different browser.

While we’re uploading chunks to the server, we know exactly how much we’ve progressed and how much there is left. We can also easily find out how fast we’re uploading. This data can be used to give feedback to the user about the progress, speed, anticipated time left, and so forth.

On the server we need to know a few things per chunk:

  1. Something that identifies the file;
  2. Something that identifies the user;
  3. The chunk’s location;
  4. The actual data.

When we have this information, we can process each chunk fairly easily. We could additionally return a checksum of the file and verify on the client that the server processed the right data. If it didn’t, the client could try resending the chunk. After the client is done uploading, it could submit a form to the server. On the server you would again need something that identifies the user (such as a session ID) to find out what exactly has been uploaded.

ASP.NET/Silverlight component development

I will go over this in a separate post, probably tomorrow.

Nice sample.
But after download the source, it couldn't run (assuming TestPage.html is the start page).
The HtmlPage.Plugin.Id is always empty and couldn't find "_xaml" in the solution.
Do I miss any thing?
private void App_Startup(object sender, StartupEventArgs e) {
if (HtmlPage.IsEnabled) {
Action initialize = delegate {
HtmlElement xamlElement = HtmlPage.Document.GetElementById(HtmlPage.Plugin.Id + "_xaml");
if (xamlElement != null) {
InitializeFromXaml(xamlElement);
}
};
Thanks!
What's the exact code you used to put this on your page?

I was just downloaded the source and opened solution. Thought the TestPage would be start page. So you mean I should have a silverlight application and use these in it? What is the difference between Web.Silverlight and Windows.Browser?
Really want try it.
Thanks!
Ah I see. Yeah, if you just run the Silverlight project (Windows.Browser) then it won't do much by default. The easiest way to get the asyncfileupload control working is by using the ASP.NET control. You should take a look at the demo page, which contains a link to the source behind those pages (as well as the web.config).
Thank you!

But I'm having problem to try it in a silverlight app (I don't know how to create ASP app, I started straight from silverlight 2 :-)).

This is what I did:
In the downloaded solution, add a new project of silverlight application, with "web application" checked. It creates a project and a test project (SilverlightTestFileUploadApp_Web). I can ignore the project but using the test project only.

I copied the "Wilco.Windows.Browser.xap" into the testProject's clientBin,
added reference to the "Wilco.Windows.Browser.dll"

In SilverlightTestFileUploadAppTestPage.aspx point to "Wilco.Windows.Browser.xap", as the plugin source and when debug it. the
Wilco.Windows.Browser.App.App_Startup() is called but it is looking for a element with name of "pluinId"_xaml:

"HtmlElement xamlElement = HtmlPage.Document.GetElementById(HtmlPage.Plugin.Id + "_xaml");"

Which I don't know where and what that element supposed to be, in order to use the wilco.Window.Browser's file upload.

How do use it in a silverlight app?
Thank you very much!

------------------------------------------------------
SilverlightTestFileUploadAppTestPage.aspx:

<%@ Register Assembly="System.Web.Silverlight" Namespace="System.Web.UI.SilverlightControls"
TagPrefix="asp" %>

<!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" style="height:100%;">
<head runat="server">
<title>Test Page For SilverlightTestFileUploadApp</title>
</head>
<body style="height:100%;margin:0;">
<form id="form1" runat="server" style="height:100%;">
<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
<div style="height:100%;">
<asp:Silverlight ID="Xaml1" runat="server" Source="~/ClientBin/Wilco.Windows.Browser.xap" Width="100%" Height="100%" />
</div>
</form>
</body>
</html>
If you directly want to use it, you should be able to create your own Silverlight project and then reference Wilco.Windows.Browser.dll. You can then directly use the AsyncFileUpload type in your own project. Just be aware though that the uploader needs a URL to something on the server that it can upload to, and this server-side upload handler needs to work like the "UploadHandler" I implemented for ASP.NET (see the Wilco.Web.Silverlight project).
Also, if you would like to use it from ASP.NET, just reference Wilco.Web.Browser.dll in your ASP.NET project. You can then pretty much copy/paste the code from my demo pages/web.config.
Can't execute your demos
some kind of error

ErrorCode : 5014
ErrorType : ParserError
Message : illegal xml charcter
Do you have Silverlight 2 beta 1 installed, or are you using a different version?
The body of work that you have put forth on the uploader is quite impressive.

This is the first time that I have attempted to use Silverlight.

I have downloaded your code for both the Wilco.Web.Silverlight and Wilco.Windows.Browser and compiled it. That went well. When I attempted to use your asynchronous examples though, the "Browse..." button, on a click, displayed a click-type depress, but did nothing. In tracking back through the template code, the button appears to be an HTML button with no click behavior. You have a button class within the Wilco.Web.Silverlight control, but when I instantiate that instead of the HTML button by substituting:
var browseButton = new System.Web.UI.WebControls.Button();
with:
var browseButton = new Wilco.Web.Silverlight.Button();
the button is not rendered.

I presume I need to have a Wilco.Web.Silverlight.Button or a Wilco.Windows.Browser.Button in order to have the upload feature initiated with a button click.

I am new to C# as well -- but I have a C++ background. So, I may not be following the code well.

So, what do you think I am doing wrong?

Again, thanks for such a great example of building custom controls, using templates, integrating silverlight with HTML and ASP, and solving a tough problem.
Well, I started over with the original code...recompiled from scratch on another computer...and everything worked. I must have a silverlight problem on the original computer.

Again, nice work!
You might want to check what version of Silverlight you're using on both machines. Just go to any page with a visual Silverlight control on it, right click, configuration and compare the versions.
Hi:

where you change the upload path to test your dll?
The file upload doesn't work with the new Beta 2 release. Can you please point out the changes required to fix it for Beta 2.

Thanks,
Arif.
Hello Wilco

Thanks. This looks like a great piece of work but I'm having some problems compiling with SL2 RTW. I've got the the OpenFileDialog stuff in ClientServices.cs. fixed OK but am puzzled by this:

protected override void Dispose(/*bool explicitDisposing*/)

in HttpWebResponseEx. As a hack to get the code compiled I've moved the Dispose() code into Close(). Is this the RTDD?

Kind regards

Jerry
Your message will be encoded/formatted when it is displayed. If you want to post code, please put the code inside [code=X][/code] tags, where X is the language of your code (C#, ASPX, SQL, etc).
Name:
Email:
(will be encoded using JavaScript to keep it functional and prevent it from being picked up by spammers)
Url:
 
Message:
3 + 3 =