Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / CSS

Professional file uploads with the MultiUpload and ImageList server controls

1.80/5 (2 votes)
19 Dec 2007GPL35 min read 2   480  
A professional AJAX GUI for uploading files using the server controls in the Memba Velodoc XP Edition.

MultiUpload2 and ImageList in run mode

Introduction

ImageList and MultiUpload have recently been described in two articles:

Our objective is to realize a professional GUI for uploading files, and this is achieved by combining ImageList and MultiUpload. Our environment is ASP.NET 2.0 on Windows and IIS, and both our ImageList and MultiUpload controls use ASP.NET AJAX Extensions 1.0, which you can download and install from here.

Background

This article refers to the open-source controls of the “Memba Velodoc XP Edition”, which you can download from here (this page provides links to Codeplex, Google code, and Sourceforge.NET) and which are distributed under the GPL license. These controls include an ImageList and a MultiUpload controls which we use in this article. You can experiment these controls live at this link.

Using the code

In Visual Studio 2005, create a new ASP.NET AJAX-Enabled Web Site, and add a reference to Memba.WebControls.XP.dll which contains the ImageList and MultiUpload2 server controls. Memba.WebControls.XP.dll is part of the Memba Velodoc XP Edition. The source code is available at the download location cited above.

Open the Default.aspx page, and add both the ImageList and MultiUpload2 server controls, either by dragging and dropping the controls after adding them to the toolbox, or simply by adding the following code between the existing <form> tags:

ASP.NET
<form id="form1" runat="server" enctype="multipart/form-data">
<asp:ScriptManager ID="ScriptManager" runat="server">
    <Scripts>
        <asp:ScriptReference Path="~/scripts/Memba.Utils.js" />
    </Scripts>
</asp:ScriptManager>
<!-- MultiUpload2 -->
<mbui:MultiUpload2 ID="MultiUpload" runat="server"
    Text="Choose file..."
    Width="100px"
    CssClass="cssMultiUpload"
    HoverCssClass="cssMultiUploadHover">
</mbui:MultiUpload2>
<!-- MultiUpload2 -->
<!-- ImageList -->
<mbui:ImageList ID="ImageList" runat="server"
    CssClass="cssList"
    ItemCssClass="cssItem"
    ItemHoverCssClass="cssItemHover"
    ImageCssClass="cssImage"
    TextCssClass="cssText"
    RemoveCssClass="cssRemove"
    RemoveTooltip="Remove from selection"
    LinesOfText="2"
    Height="92px"
    Width="420px">
</mbui:ImageList>
<!-- ImageList -->
<input type="button" id="ClearButton" value="Clear" onclick="onClear();" />
<asp:Button ID="SubmitButton" runat="server" Text="Submit" OnClick="SubmitButton_Click" />
</form>

This form also includes an HTML button (ClearButton) and an ASP.NET Submit button (SubmitButton).

You may have to register the tag prefix at the top of the page, using the following statement:

ASP.NET
<%@ Register Assembly="Memba.WebControls.XP" 
    Namespace="Memba.WebControls" TagPrefix="mbui" %> 

Make sure you add a script reference to Memba.Utils.js, which contains a necessary utility function to create GUIDs in JavaScript.

For file uploads to work, you need to make two important settings:

  1. Define enctype="multipart/form-data" on the form tag;
  2. Add an httpRuntime section to the <system.web> section of your web.config. This is extensively described in the articles referenced in the introduction above.

The inline CSS styles used for the presentation of ImageList and MultiUpload are also described in the corresponding articles. After adding the styles, your page in Design mode should look like:

MultiUpload2 and ImageList in design mode

Now that our page is designed, we need the code to save the posted files. Double click the Submit button (btnSubmit) to implement the server-side Click event handler in Default.aspx.cs:

C#
private const string TARGET_DIR = "~/";
 
protected void SubmitButton_Click(object sender, EventArgs e)
{
    //Check target directory
    string sTargetDir = Request.MapPath(TARGET_DIR);
    System.Diagnostics.Debug.Assert(Directory.Exists(sTargetDir));
 
    //Iterate through posted files
    for (int i = 0; i < Request.Files.Count; i++)
    {
        HttpPostedFile objFile = Request.Files[i];
        //Make sure file input has content
        if ((objFile.ContentLength > 0) && (objFile.FileName.Length > 0))
        {
            //Get target file path for save as
            string sFileName = Path.GetFileName(objFile.FileName);
            string sFilePath = Path.Combine(sTargetDir, sFileName);
            FileInfo objFileInfo = new FileInfo(sFilePath);
            //No business rule, i.e. we just want to avoid failure
            if (objFileInfo.Exists)
            {
                objFileInfo.Attributes &= ~FileAttributes.ReadOnly;
                objFileInfo.Delete();
            }
            //Save file
            objFile.SaveAs(sFilePath);
        }
    }
    //Clear the list, otherwise the ImageList has items
    //Which the MuliUpload component does not have
    ImageList.ImageListItemCollection.Clear();
}

Add the following statement at the top of the file:

C#
using System.IO; //Directory, Path, FileInfo 

The important thing to note here is that the browser clears HTML file input controls each time it loads the page, especially after a postback. There is no way to set the value of a file input control, otherwise web servers could obtain user files without their consent. There is no workaround to this security restriction. Our ImageList has been designed to maintain its state, so it needs to be cleared to be in sync with the MultiUpload control.

We could run the page at this stage and upload files, but we need some JavaScript client code to display, in the ImageList, the files which have been selected in the MultiUpload control. Add the following script just before the </body> closing tag of your page:

JavaScript
<script type="text/javascript">
<!--
// Declare global variables for the various controls
var g_MultiUpload;
var g_ImageList;
//pageLoad function of ASP.NET Ajax Extensions framework
function pageLoad()
{
    //Get a reference to the MultiUpload control and
    //add en event handler for the browse event
    g_MultiUpload = $find("<%= MultiUpload.ClientID %>");
    if(g_MultiUpload)
        g_MultiUpload.add_browse(onBrowse);
    //Get a reference to the ImageList control and
    //add en event handler for the browse event
    g_ImageList = $find("<%= ImageList.ClientID %>");
    if(g_ImageList)
        g_ImageList.add_remove(onRemove); 
}
//pageLoad function of ASP.NET Ajax Extensions framework
function pageUnload()
{
    if(g_MultiUpload)
        g_MultiUpload.remove_browse(onBrowse);
    if(g_ImageList)
        g_ImageList.remove_remove(onRemove); 
}
//Event handler for the browse (click) event of the MultiUpload control
function onBrowse(sender, args)
{
    if((g_ImageList) && (g_MultiUpload))
    {
        //Search for the item in the Imagelist
        if (g_ImageList.find_item(args.get_value()).length > 0)
        {
            alert("file already in list");
            //The item already exists,
            //we can remove the duplicate INPUT in the MultiUpload control
            g_MultiUpload.removeInput(args.get_id());
        }
        else
        {
            //Since the item is not found in the ImageList,
            //create a new item
            var item = new Memba.WebControls.ImageListItem(
                Memba.Utils.newGuid(),
               '<%= this.ResolveClientUrl("~/images/upload.gif") %>',
               args.get_value(),
               args.get_value(),
               args.get_id()
               );
            //Add the new item to the ImageList
            g_ImageList.add_item(item);
        }
        //We can do some tracing which will display in the TraceConsole textarea 
        Sys.Debug.trace(g_ImageList.get_count()
            + " files in image list, and " + g_MultiUpload.get_count()
            + " files in MultiUpload control");
    }
}
//Event handler for the remove event of the ImageList control
function onRemove(sender, args)
{
    if((g_ImageList) && (g_MultiUpload))
    { 
        //Upon clicking the remove icon in the ImageList,
        //remove the corresponding INPUT in the MultiUpload control
        g_MultiUpload.removeInput(args.get_tag());
        //We can do some tracing which will display in the TraceConsole textarea 
        Sys.Debug.trace(g_ImageList.get_count()
            + " files in image list, and " + g_MultiUpload.get_count()
            + " files in MultiUpload control");
    }
}
//Event handler for the click event of the clear button
function onClear()
{
    if(g_MultiUpload)
        g_MultiUpload.clear();
    if(g_ImageList)
        g_ImageList.clear();
}
//-->
</script>

ASP.NET AJAX extensions provide two important JavaScript event handlers:

  • pageLoad is called by the framework when the page DOM and scripts are loaded and initialized. This is a good place to get references to controls and add event handlers.
  • pageUnload is called by the framework when the page unloads. It is recommended to clear handlers at this stage.

The script above implements event handlers for the Browse event of the MultiUpload control, for the Remove event of the ImageList control, and for the Click event of the Clear button. The Browse event handler adds the selected file to the ImageList. ImageList items have a property called tag, which is set to the identifier of the file input in the MultiUpload control. The Remove event handler reads the tag of the item removed from the ImageList to remove the corresponding file input from the MultiUpload control. The Clear event handler simply clears both the ImageList and the MultiUpload controls.

Press F5 to run the project, and click the MultiUpload control labeled “Choose file...” to add a file to the list. Move your mouse over the corresponding item in the ImageList to display the remove icon which is a red cross. Click the remove icon to remove the corresponding file from the upload selection. Add more files. Test the Clear button. Add files, and submit to upload the files.

At this stage, finalizing a professional upload page would require a progress indicator. We recommend reading Using AJAX to display the progress of an ASP.NET server task. If you want to combine all these controls together, I recommend downloading the source code of the Memba Velodoc XP Edition and the developer tutorial included, which will help you get to the next stage.

Points of interest

This MultiUpload control hides the complexity of dynamically creating file input HTML controls, and removes any constraint regarding the presentation of the files which have been selected for upload. The combination with the ImageList control is a great presentation option described here, which is a huge improvement over old, ugly file input controls. For more advanced developers, the source code for the ImageList and MultiUpload controls is available at Codeplex, Google code, and Sourceforge.NET, and is commented in great details.

History

  • Version 1.0 - Dated 20 Dec 2007.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)