Print This Post

ASP.Net File Upload Revisited - Part 4, UI and other enhancements

This is the fourth and final post intended for users of version 1 of the ASP.Net file upload component. The next release of the component includes a completely rewritten user interface with custom file upload controls in addition to the client side AJAX progress bar. In addition to this the modalbox javascript library has been used to enhance the look and feel of the progress bar. There is also a cancel button, file extension filtering, and a list of other enhancements.

Let’s get started with the new file upload control. The file upload module will work with any RFC 1867 compliant file upload. This of course includes the standard ASP.Net FileUpload control. In fact, version 1 of the component did not provide any UI enhancements to this beyond the progress bar which was provided by the UploadProgress server control. In this release a new server control has been provided which offers several advantages over the standard control- although you don’t have to use it as standard file inputs will continue to work just fine. The upload control allows multiple file uploads per control up to a preconfigured maximum as shown in the screen shot below.

The file upload controls are styled using a technique I found on www.quirksmode.org. This is a javascript method of skinning file input controls. I’ve extended this slightly to allow file extension checking and also to tidy up the display of the file names in input controls so that the path is stripped out. The skin of the control is easily changed using a combination of CSS and graphic buttons, so it should be simple to make it fit in with your site.

Each file input has a remove button next to it which will clear the file from the input when it is clicked. This is a surprisingly difficult thing to do as the value of file inputs cannot be set. This is an understandable security precaution but it would be nice to at least be able to clear them. In the end I opted for removing the input from the DOM before re-adding it to clear the value. I know this works in IE and Firefox, but further testing is required on other browsers.

Each upload control can optionally have an add button and submit button. Actually any submit button on the page will cause the file upload to start, but it’s sometimes nice to have a button next to the control in question. Both buttons can be turned on or off via properties on the control.

Also new to this version is the addition of file extension filtering. A single property is provided which accepts a comma separated list of file extensions. When this is set the user will not be allowed to select any files which do not match one of the extensions.

Each page can have as many file upload controls as required, but each page must have one instance of the DJUploadController control added to it before any file upload controls. This is a new feature in version 2 and provides a single point where all upload controls are managed and coordinated at run time. This control is responsible for all javascript and resources which are required by the upload controls and also manages the display of the progress bar and the upload status key.

The upload status key is a session identifier for uploads which allows the controller to get the correct status information for an upload as it is happening. To get this information the controller uses javascript to call a web service, passing in the unique identifier of the upload. The status key must match a value which is used by the upload module to store status information. In the previous version of the module, the status key was recovered from the URL during an upload. The key was placed in the URL by changing the form action using javascript prior to the form being submitted. In that way the module and the form could be in sync allowing status information to be passed back. This method works fine but it does leave a nasty guid on the URL. This has been replaced in version 2. Instead of using the URL, a hidden field is placed on the form by the upload controller. This is then pulled from the request by the new RFC 1867 parser. The trade of it that there can be a very slight delay in getting status information whilst the parser finds the status key, but this is outweighed by the advantage of not changing the URL of the request.

When the form is submitted, the upload controller uses javascript and XMLHttp to communicate with the server and get progress information. It does this on a timer whilst the upload is progressing and updates a progress bar accordingly. One problem with version 1 of the control was that whilst the upload was in progress, controls on the page could still be clicked. To improve on this I’ve used the modalbox javascript library to present the status information using a light box effect- which I think looks a bit better and also means that screen space does not have to be reserved for the progress bar on the form.

The cancel button on the modal window can be turned on or off via a property on the upload controller. The button simply causes the page to refresh. This in effect cancels the request which is picked up by the module causing the appropriate actions (e.g. rolling back the transaction) to happen on the server. From the user’s point of view they are simply returned to the page they started on with the upload controls cleared.

Once the upload has completed the Status property of the controller can be used to retrieve a list of all files which were uploaded and any where errors were encountered. The file name is available along with any unique identifier provided by the file processor. In the event of an error the exception is provided.

protected void Page_Load(object sender, EventArgs e)
{
    if (Page.IsPostBack && DJUploadController1.Status != null)
    {
        StringBuilder sb = new StringBuilder();

        sb.Append("<div class='up_results'>");
        sb.Append("<h3>Files uploaded</h3>");
        sb.Append("<ul>");

        foreach (UploadedFile f in DJUploadController1.Status.UploadedFiles)
        {
            sb.Append("<li>");
            sb.Append(f.FileName);

            if (f.Identifier != null)
            {
                sb.Append(" ID = ");
                sb.Append(f.Identifier.ToString());
            }

            sb.Append("</li>");
        }

        sb.Append("</ul>");

        sb.Append("<h3>Files with errors</h3>");
        sb.Append("<ul>");

        foreach (UploadedFile f in DJUploadController1.Status.ErrorFiles)
        {
            sb.Append("<li>");
            sb.Append(f.FileName);

            if (f.Identifier != null)
            {
                sb.Append(" ID = ");
                sb.Append(f.Identifier.ToString());
            }

            if (f.Exception != null)
            {
                sb.Append(" Exception = ");
                sb.Append(f.Exception.Message);
            }

            sb.Append("</li>");
        }

        sb.Append("</ul>");
        sb.Append("</div>");
        ltResults.Text = sb.ToString();
    }
}

In general there is considerably more information available about uploads than in version 1, particularly with the ability for processors to add their own identification information which made the SQLProcessor possible.

The user interface elements have so far been tested in Internet Explorer 6-7 and Firefox. Safari testing is underway now, but I’m not expecting any real problems (touch wood).

Migration

Migration from version 1 to version 2 should be very straight forward. The IFileProcessor interface has changed, but not significantly. The UploadProgress control has been removed and replaced by the DJUploadController control which needs to be placed on each page- so that will be something to watch out for.

I’ll be making the beta version of the control available for download in a couple of days. That release will include a demonstration project as well as all of the source code. The same license (LGPL) will be used as version 1. When I upload the beta I’ll post installation instructions and developer notes. In the meantime thanks to everyone who has posted feedback and helped with testing.

There Are 2 Responses So Far. »

  1. Hi Darren,

    Is it possible for me to limit the size of the file for a transaction?

    Thanks,
    Buhari

  2. Hi Buhari,

    You can use the ASP.Net request limits or if you need to limit for an individual transaction you can do so in your IFileProcessor implementation- just throw an exception if a set number of bytes is exceeded.

    Thanks,
    Darren

Post a Response