Multiple File Upload
I’ve always found it unpleasant when I have a number of files to upload and the website I’m at only allows one file upload at a time. If you have multiple files to process it can seem like an eternity. Just ask my wife when she’s uploading all those family photos to her blog.
One possible solution is to place several file input controls on the page (say five) thereby reducing the number of roundtrips to the server, but that is still an artificial limit. What I wanted was a control that would dynamically allow any number of files to be uploaded all in one shot. Something that would increase usability and not take away from it.
Here’s what I came up with:

It’s really quite simple. The basic principle is that each time the user browses for a file to upload and clicks the “Add” button a new input element is dynamically created. The input field that was just worked on by the user is hidden with a CSS style and the newly created one is put in its place. At the same time, an option is added to the select box to represent the file queued for upload and to keep track of the “hidden” input fields. All this magic happens dynamically with JavaScript and is so fast that it’s imperceptible to the user. I suppose I didn’t need to hide the input controls and could have just display them visibly on the page, but this looks way cooler!
If you’re wondering why I am using this approach, you can get more information by reading some of the articles linked below. To summarize:
- The file input field is the only way to get files from the browser to the server.
- The file input field can’t be manipulated with JavaScript for security reasons; otherwise, you could pre-populate the input field with any arbitrary file when the page is loaded and cause a file to be sent from the user’s computer to the server without them knowing!
With those limitations in mind, the file to upload must be specified by the user (and not by JavaScript) and in order to specify more than one, more input controls must be created. There are a number of ways this could be done and this is simply the way I liked.
Here’s the JavaScript for the “Add” and “Remove” button clicks (the first few lines of each function are just to locate the elements to work with):
function AddFileInput() { var selectFileList = document.getElementById('selectFileList'); var tdFileInputs = document.getElementById('tdFileInputs'); var fileInput = tdFileInputs.lastChild; // Make sure we have a selected file if(fileInput.value == null || fileInput.value.length == 0) return; // Create a new file input var newFileInput = fileInput.cloneNode(false); newFileInput.value = null; newFileInput.id += 'A'; // A unique id newFileInput.name = newFileInput.id; tdFileInputs.appendChild(newFileInput); // Hide the file input and add an entry to the select box fileInput.style.display = 'none'; selectFileList.options[selectFileList.options.length] = new Option(fileInput.value, fileInput.id); } function RemoveFileInput() { var tdFileInputs = document.getElementById('tdFileInputs'); var selectFileList = document.getElementById('selectFileList'); // Make sure we have a selection if(selectFileList.options.length == 0 || selectFileList.selectedIndex < 0) return; // Remove the option from the select file input element specified by the option var fileInputId = selectFileList.options[selectFileList.selectedIndex].value; selectFileList.options[selectFileList.selectedIndex] = null; // Remove the file input var fileInput = document.getElementById(fileInputId); tdFileInputs.removeChild(fileInput); }
With the JavaScript in place, all the work for making and keeping track of the files to be uploaded is done. From the server side it’s even easier. ASP.NET provides a collection of HttpPostedFile objects that represent each file that comes up in the request. There should be one for each of the files added to the queue (because remember there’s also a hidden file input field for each item). Here’s a quick example of how to access those files and spit out some data about them:
protected void btnUpload_Click(object sender, EventArgs e) { StringBuilder sb = new StringBuilder(); HttpFileCollection files = Request.Files; for (int i = 0; i < files.Count; i++) { HttpPostedFile file = files[i]; if(file.ContentLength <= 0) continue; sb.Append("File Name: "); sb.Append(file.FileName); sb.Append("<br />"); sb.Append("File Content Type: "); sb.Append(file.ContentType); sb.Append("<br />"); sb.Append("File Size: "); sb.Append(file.ContentLength); sb.Append("<br />"); sb.Append("<br />"); } ltrlMessage.Text = sb.ToString(); }
The one last thing to be aware of is that the enctype attribute of the form tag must be set to “multipart/form-data”:
<form id="form" runat="server" method="post" enctype="multipart/form-data">I’ve included a basic sample in the zip file below. It should be plenty to get anyone going on their own solution. Some other things to consider might be setting an artificial limit on the number of files a user can upload so that they don’t bring down your server. Also you might want to increase the maximum request size and timeout the application can handle. This change can be made in the web.config and more information about that is in the linked articles.
Cheers.
Source Code
- MultiUpload.zip (18.5 KB)
Resources
- Upload multiple files with a single file element - A blog entry that demonstrates the file input substitute technique I used.
- Uploading Files Using the File Field Control - MSDN article on some of the ASP.NET facilities for file uploads.
- Upload multiple files Hotmail style - An article that served as inspiration but roundtrips to the server.
September 15th, 2007 at 5:04 am
If I understood this, it would make my life much easier!
October 4th, 2007 at 4:01 am
Thanks a lot for the code! It works perfeclty, only one small thing,The liste box loses its values after postbacks from other controls! Can you please help!!!
October 4th, 2007 at 8:28 pm
I think you are asking either one of these two questions to which these are my answers:
Question: How can I keep the list box contents round trip to the server?
Answer: Easy, just make it a ListBox instead of a select element and regular ASP.NET web handling should get you the contents back.
Question: How can I keep the actual files queue maintained roundtrip to the server?
Answer: You can’t for various reasons. The first of which is the fact that we’re creating file input controls client side and those won’t persist roundtrip. That could be overcome by tracking the creation of those controls and reporting back to the server on postback but even so you still have a bigger problem. As I mentioned you can’t preload a file input control with a path for security reasons which means that on every postback the contents of the file input controls are going to be wiped out. So even if you could persist the contents of the list box or the file input controls back to the server you still would have empty file input controls on every page load and no way to put the file paths back in.
My suggestion is to try and rework your page so that it’s clear to your users that if they postback in any other way than the Upload button (i.e. - via some other control on your form) that they will need to reload their queue. This would be of benefit to them and to you because the queued files are always going to get posted back to the server whether you’re using the Upload button or not. A postback is a postback regarless of which button it comes from and any queued files will get sent over the wire (needlessly).
Short answer, it’s better not to postback unless you intend to upload files.
Cheers.
October 24th, 2007 at 4:32 am
Neat solution. Thank you
October 30th, 2007 at 2:19 pm
Thanks, as Vishal says it is a Neat solution for a irritating problem.
November 2nd, 2007 at 7:20 pm
Hi …thanks for the code.But can u tell how to handle the duplicate files problem.
November 5th, 2007 at 2:53 pm
I don’t know what your requirements are, but checking the file paths for duplicates is probably sufficient to weed out duplicate files. You could easily do this by adding a CustomValidator to the form and supply it with a client side function that would compare the file paths.
Beyond that you could also compare the file sizes when they arrive at the server or if you went to the extreme, compare hashes of each file on the server.
February 1st, 2008 at 5:19 am
Excellent. Exactly what I was looking for. Will the client side script run on any browser? For example, FoxFire and Netscape in addition to Explorer. Thanks.
February 13th, 2008 at 3:26 am
Even though I originally wrote this for a company that only supports IE, I’m pretty good at testing my JavaScript in Firefox. I would be surprised if it didn’t work.
April 29th, 2008 at 5:35 pm
Hi, I test your code and for an strange and unknow reason the FileCollection is empty in the PostBack.
Just if I add a FileUpload control somewhere in the form I can get the correct FileCollection.
I’m using exactly your page in the example. nothing less, nothing more.
I’m working in Vista64, VS2008, Framework 3.5.
Did you have any idea ??
Thanks.
May 3rd, 2008 at 6:34 pm
If I understand you correctly, it sounds that the FileCollection is empty when using the sample code, but works when you add a FileUpload control to the page manually. Is that correct?
If that assumption is correct, I would assume then that there may be a problem with the JavaScript and the dynamic creation of the upload elements. I would double check that the script is running correctly in your browser to make sure those elements are actually being created at all. That might explain why the FileCollection is empty.
May 8th, 2008 at 1:18 pm
Re: Empty FileCollection
Check the enctype of the Form is set to “multipart/form-data”.
You might need to set it manually in the code,
e.g. this.Page.Form.Enctype = “multipart/form-data”;
PS. Nice solution.
Thanks
May 29th, 2008 at 1:25 pm
Hi Jacob,
I use your sample code, after continue; I put
string fileName = Path.GetFileName(file.FileName);
fileInput.PostedFile.SaveAs(Server.MapPath(”~/upload/” + fileName));
to upload files.
Works well, except if I remove first file(item) in the listbox (there’re more than one items in the box), then click ‘Upload’, I get a ‘System.NullReferenceException’ error.
If I use asp.net 2.0 file upload control, and use
fileInput.SaveAs(…);
Once I remove the fist item, no error message but no file have been uploaded at all.
If I remove items other than the first one, then uploading is OK.
I debug through the code found that after I remove the first item, the PostedFile is null.
Do you have any suggestion about this error?
Thanks
May 29th, 2008 at 9:00 pm
It’s been a while since I used this code, but I seem to remember not using the ASP.NET FileUpload control because it just complicated things. If your only reason for using the control is for its save functionality, there is a SaveAs method already on the HttpPostedFile.
Other than that, I might suggest that there is something wrong with my JavaScript that is causing the input element to remain in the page but without having a file to upload. This might produce a null file in the collection
If all else fails, you might try changing the continue statement to also check for null. i.e. -
if(file == null || file.ContentLength <= 0)
continue;
Jacob