Uploading a Variable Number of Files from an ASP.NET Web Page

Published 31 March 12 03:15 AM | Scott Mitchell

ASP.NET has long offered the FileUpload control, which simplifies uploading a file from the client’s computer to the web server’s file system. This control provides a very simple, straight-forward user experience – the browser renders a “Browse” or “Select File” button that, when clicked, brings up a dialog box from which the user can select a file from her computer. Behind the scenes, the FileUpload control renders as an <input type=”file” /> HTML element and sets the WebForm’s encoding type to multipart/form-data. The <input type=”file” /> HTML element is what instructs the browser to render the file upload interface and the multipart/form-data encoding type instructs the browser to send the form’s contents (including the binary contents of the selected file) to the server as a multipart MIME message, which allows for transmission of binary data.

Recently, I was working on a web page where users needed the ability to upload a variable number of files. While most users would only upload a single file, some would need to upload two, while others three, four, or possibly more. Since a single FileUpload control can upload only a single file, one option was to add four (or five or six) FileUpload controls on the page, but that would crowd the page and be overkill for the majority of users who only needed to upload a single file. We considered using a third-party file upload control (such as SlickUpload) that would supports richer upload scenarios, including multifile uploads, but ended up with a simpler solution that involved a bit of HTML and jQuery.

In particular, we had the page initially contain a single <input type=”file” /> HTML element. Next, we added an “Upload another file” link that, when clicked, executed JavaScript that dynamically added another <input type=”file” /> HTML element to the DOM. Additionally, we added a “Remove” link next to each <input type=”file” /> HTML element that, when clicked, removed the associated file upload user interface from the DOM.

Show Me!

Here is a demo of this concept. First the HTML. Note that there is a form whose enctype attribute is explicitly set to multipart/form-data. Inside the form is a paragraph with an id of uploadUI and an “Upload another file” link with the id of addFileUpload. There’s also a Button Web control (btnUpload) that, when clicked, will postback the form, submitting the file contents to the server. We’ll see how to access the uploaded files from server-side code shortly.

<form id="form1" runat="server" enctype="multipart/form-data">
    <p id="uploadUI">
            
    </p>
    <p>
        [<a href="#" id="addFileUpload">Upload another file...</a>]
    </p>
    <p>
        <asp:Button ID="btnUpload" runat="server" Text="Upload Files" 
            onclick="btnUpload_Click" />
    </p>
</form>

The bulk of the content in this page is the JavaScript that creates and removes the <input type=”file” /> HTML elements in response to the user clicking the “Upload another file” and “Remove” links. Each file upload user interface is comprised of:

  • A <div> element with an id of fileContainerCount, where Count is a variable that starts at 0 and is incremented each time a new file upload interface is added to the DOM. This <div> element contains the following:
  • An <input type=”file” /> HTML element
  • A “Remove” link that has a class attribute value of removeUpload

The file upload user interface is added by the createFileUpload function. The <div> element (and its containing elements) are added to the end of the uploadUI paragraph.

var uploadControlCounter = 0;
function createFileUpload() {
    $("#uploadUI")
        .append(
            $("<div />")
                .attr("id", "fileContainer" + uploadControlCounter)
            .append(
                $("<input />")
                    .attr("type", "file")
                    .attr("name", "file" + uploadControlCounter)
            )
            .append(" ")
            .append(
                $("<a />")
                    .attr("href", "#")
                    .attr("class", "removeUpload")
                    .text("Remove")
            )
        );

    uploadControlCounter++;
}

In the $(document).ready event handler I wire up the click event handlers for the “Upload another file” and “Remove” links, as well as add the initial file upload user interface. Note that for the “Remove” links I use the delegate behavior for jQuery’s on function because I want this event handler to fire for not just the current “Remove” links (which there are none), but for all “Remove” links that may be added to the DOM at a later point in time.

$(document).ready(function () {
    $("#addFileUpload").click(function (e) {
        e.preventDefault();
        createFileUpload();
    });

    $("#uploadUI").on("click", "a.removeUpload", function (e) {
        e.preventDefault();
        $(this).parent().remove();
    });

    // Start by creating one file upload
    createFileUpload();
});

Onto the Server-Side…

When the user clicks the “Upload Files” button their browser will make a request back to the page sending the selected files’ contents. We can inspect the set of uploaded files on the server-side by looping through the Request object’s Files collection. The Request.Files property is a collection of HttpPostedFile objects, which have properties that provide the file name, content type, content length, and a stream to the raw binary data. In my demo I loop through the Request.Files collection and for each file save it to the ~/Uploads folder, but since you have access to the raw uploaded binary data via the HttpPostedFile object’s InputStream property you could do whatever it is you needed to do, such as save the contents to a database, inspect the contents in memory, and so on.

// Save each upload and redirect user to thank you page
for (int i = 0; i < Request.Files.Count; i++)
{
    HttpPostedFile file = Request.FilesIdea;

    string filePath = Server.MapPath("~/Uploads/" + Path.GetFileName(file.FileName));

    using (FileStream streamToDisk = File.Create(filePath))
    {
        file.InputStream.CopyTo(streamToDisk);
        streamToDisk.Close();
    }
}

That’s all there is to it! You can download the complete demo from http://scottonwriting.net/demos/MultiFileUpload.zip

Happy Programming!

Comments

No Comments

Archives

My Books

  • Teach Yourself ASP.NET 4 in 24 Hours
  • Teach Yourself ASP.NET 3.5 in 24 Hours
  • Teach Yourself ASP.NET 2.0 in 24 Hours
  • ASP.NET Data Web Controls Kick Start
  • ASP.NET: Tips, Tutorials, and Code
  • Designing Active Server Pages
  • Teach Yourself Active Server Pages 3.0 in 21 Days

I am a Microsoft MVP for ASP.NET.

I am an ASPInsider.