Example Usage of the Silverlight File Uploader

Writing an application that allows users to upload files typically flows as follows:
  1. The user chooses files and begins uploading
  2. The client code starts sending the files to the server, showing the user some progress indicators
  3. The server receives the files bit by bit, usually storing the incomplete file in some temporary location
  4. When a file has been completely sent to the server, the server processes the file -- this can simply be moving it to its final destination, inserting it into a database, or could involve a more complex process such as parsing a text file, manipulating an image, etc.
  5. Report success to the user, and perhaps have the rest of the web page respond to completion

This article will walk through the example code provided for this project, relating each piece to the general steps listed above.

Steps 1 and 2: The UI

An ASP.NET server control called UploaderControl has been supplied to facilitate setting the parameters of the Silverlight control, which contains the UI and parameters that specify how the client will send files. Place the following into your aspx page to create an uploader that is 600x400 pixels in size:

<div style="width:600px;height:400px;">
    <vci:UploaderControl ID="uploader1" runat="server" />
</div>
That's it! See the comments in UploaderControl for information on the various options available.

Step 3: Receiving the Uploading Files

The files are sent to the server in chunks, so you will need a temporary location on the file system to write the uploading file contents. A handler called UploaderControlHandler.ashx has been provided in the example code. By default, the silverlight control rendered by UploaderControl will send files to this handler. This URL uses the code defined in UploaderControlHandler.cs in the Vci.FileUploader library. This handler writes the files to a directory called the "sandbox". This can be configured in your web.config file:

<appSettings>
  <!-- path to a folder where files will be uploaded; the web application must have write access to this folder -->
	<add key="SandboxPath" value="E:\VCI\Sandbox"/>
</appSettings>
You must make sure that your web application has write access to this folder (ASPNET or NETWORK SERVICE user, depending on your OS/IIS version). It is recommended that this folder exist somewhere outside of your web application's directory (i.e. not a virtual directory underneath your application path), as adding/modifying files can cause the web application to restart.

The example code provides a class called Vci.Core.Sandbox to facilitate accessing this location on disk.

Step 4: Processing a Completed File

When a file has been completely uploaded, you'll invariably want to process it -- stick it into a database, create some thumbnails, parse its contents, etc. One approach would be to modify the handler to suit your needs... but that's a pain -- that code needs to know a bunch of stuff about how the Silverlight control uploads files, and deal with reading the chunks, etc. We have provided a mechanism for specifying a post-processor for uploaded files. On the UploaderControl web control, you can specify an uploaded file processor, which is a type that implements the provided interface IUploadedFileProcessor:

<vci:UploaderControl ID="uploader1" runat="server" UploadedFileProcessorType="Vci.FileUploader.ExampleFileProcessor,Vci.FileUploader" />
If you have specified this property, when a file has been completely uploaded, the provided UploaderControlHandler.ashx will invoke the ProcessFile method of your IUploadedFileProcessor implementation:

public class ExampleFileProcessor : IUploadedFileProcessor
{
    public void ProcessFile(HttpContext Context, string FileGuid, string FileName, string ContextParam)
    {
        string sandboxPath = Path.Combine(Sandbox.UploaderControlSandboxPath, FileGuid);

        using (FileStream fs = File.OpenRead(sandboxPath))
        {
            // do something useful to the file
        }

        // for testing, this example just deletes the file from the sandbox after it has been uploaded; comment
        // out this line if you want to verify that files are being uploaded correctly while testing
        File.Delete(sandboxPath);
    }
}

Step 5: Responding to Completion on the Web Page

It is probable that you will want the web page that contains the file uploader to respond to various events. We have provided a javascript object that wraps the Silverlight uploader and provides an easy mechanism for handling events. Here is an example of handling the onfileuploaded event:

        window.onload = function() {

            // get the instance of UploaderControl that corresponds to the uploader1 server control
            var uploader1 = UploaderControl.getInstance("uploader1");
            
            // attach event handlers
            uploader1.attachEvent("onfileuploaded", uploader1_onfileuploaded);
        };
        
        // do some custom js code when each file has completed uploading
        function uploader1_onfileuploaded(fileGuid, fileName, fileSize) {
            document.getElementById("divDebug").innerHTML += "file uploaded: " + fileGuid + " " + fileName + " " + fileSize + "<br/>";
        }
That's it -- you don't have to screw around with accessing the Silverlight object directly, the javascript object UploaderControl insulates you from any complexities, making your javascript handling very robust.

Miscellaneous Feature: 'User Context'

An important miscellaneous feature is called the 'user context'. When a user begins an upload session, it is very probable that you will need to send a piece of context information. For instance, which page is the user on? Has the user selected an element on the page for which he is uploading files? And so on. This is handled via the setUserContext javascript methods provided on the UploaderControl javascript object. Here is an example of how to do it:

        window.onload = function() {

            // get the instance of UploaderControl that corresponds to the uploader1 server control
            var uploader1 = UploaderControl.getInstance("uploader1");
            
            // attach event handlers
            uploader1.attachEvent("onuploadstarted", uploader1_onuploadstarted);
        };
        
        // when an upload is started, set a context parameter that will be passed down to the ExampleFileProcessor
        function uploader1_onuploadstarted() {
            var uploader1 = UploaderControl.getInstance("uploader1");
            uploader1.setUserContext("some identifying information, like the user name, or whatever");            
        }
When the server handler or your file processor is invoked (see step 4 above), this piece of information will be provided.

Important Note on the Http Handler

The example http handler provided can largely be used as-is for your production code, with one small exception. It is an asynchronous handler, because processing a file could be a time-consuming operation that is not CPU-bound. (If you do not do any file processing, or your processing is very CPU-intensive, you can probably leave the code as-is. If you have a very low-traffic site, you can also leave the code as-is. Otherwise, read on).

The example uses the built-in ThreadPool to do the asynchronous processing, which you should NOT do in a production environment. You should use a custom thread pool. The reason for this is that the built-in ThreadPool draws from the same threads used to process web requests. Thus, you don't gain any improved scaling by using up those threads for your file processing. The custom thread pool that we use cannot be redistributed in our own open source project, so here are some links to good open-source thread pools: There are plenty of comments in UploaderControlHandler.cs that indicate where you would want to make this change. I have used the first link above and had no problems. The second link looks pretty solid too.

Last edited Jun 18, 2009 at 7:23 PM by pcoley, version 5

Comments

NickHeiner Sep 12, 2010 at 1:44 PM 
Looks great. Can this be embedded into my SL app, like a TextBox, or must it be a stand-alone app?