Upload Multiple Files With Progress Using Uploadify
I previously posted on how to use Uploadify to upload files. The previous post covered really well how to upload single files to an ASP.NET MVC controller using Uploadify. This post will expand on that a bit. First we will revisit how to upload multiple files using a single HTML input element. Then the case of uploading multiple files using multiple HTML input elements. Finally, how to upload multiple files while posting form data.
Here are links to the demo pages for the Single and Multiple file uploads.
Getting Started
If you are not familiar with Uploadify here is a description from their web site:
Uploadify is a jQuery plugin that integrates a fully-customizable multiple file upload utility on your website. It uses a mixture of JavaScript, ActionScript, and any server-side language to dynamically create an instance over any DOM element on a page.
If will first need to download the Uploadify code. I simply unzipped it into my ‘scripts’ folder as shown below:
Any page that uses Uploadify will need to include the following lines in the ‘head’ section:
<link href="<%= Url.Content("~/Content/Scripts/uploadify/uploadify.css") %>" rel="stylesheet" type="text/css" /> <script type="text/javascript" src="https://bobcravens.com/wp-content/uploads/2011/03/jquery-1.4.1.js"></script> <script type="text/javascript" src="<%= Url.Content("~/Content/Scripts/uploadify/swfobject.js") %>"></script> <script type="text/javascript" src="<%= Url.Content("~/Content/Scripts/uploadify/jquery.uploadify.v2.1.4.min.js") %>"></script>
The first line links in the CSS used by Uploadify. The last three lines add the required JavaScript.
Multiple Files With A Single HTML Input
Uploadify directly supports uploading multiple files by configuring the Uploadify object. Add the following HTML input elements to your page:
<p><input type="file" id="multipleFiles" /></p> <p><button id="btn1">Upload Files</button></p>
The ‘input’ element will be used as the Uploadify object. The ‘button’ element serves as a trigger to start the uploads. Here is the JavaScript that configures this scenario:
// Multiple files - single input $("#multipleFiles").uploadify({ 'uploader': '<%= Url.Content("~/Content/Scripts/uploadify/uploadify.swf") %>', 'script': '<%= Url.Action("Upload") %>', 'fileDataName': 'file', 'buttonText': 'File Input...', 'multi': true, 'sizeLimit': 1048576, 'simUploadLimit': 2, 'cancelImg': '<%= Url.Content("~/Content/Scripts/uploadify/cancel.png") %>', 'auto': false, 'onError': function (a, b, c, d) { if (d.status == 404) alert("Could not find upload script. Use a path relative to: " + "<?= getcwd() ?>"); else if (d.type === "HTTP") alert("error " + d.type + ": " + d.status); else if (d.type === "File Size") alert(c.name + " " + d.type + " Limit: " + Math.round(d.info / (1024 * 1024)) + "MB"); else alert("error " + d.type + ": " + d.text); }, 'onComplete': function (event, queueId, fileObj, response, data) { alert(response); } }); $("#btn1").click(function () { $('#multipleFiles').uploadifyUpload(); });
The Uploadify object supports a number of properties, events, and methods. Visit their page for in-depth information. The configuration that allows multiple files to be selected is the ‘multi’ option. Setting this to ‘true’ enables multiple file selection as shown below.
That results in the following:
The ‘Upload Files’ button is wired up to call the ‘uploadifyUpload’ method on the Uploadify object. This begins the upload process. The file uploads happen asynchronously and not in a guaranteed to arrive in a specified order. All selected files are handled by a single ASP.NET MVC action method:
public string Upload(HttpPostedFileBase file) { // Process the file here. // return "Upload processed. filename=" + file.FileName; }
This works great if the same processing can be applied to all files. If you require the files to be dispatched out to different processors based upon their type then this probably will not work. In this case, you will need to use multiple HTML input elements.
Multiple Files With Multiple HTML Input Elements
Now we have multiple HTML ‘input’ elements on the page.
<p><input type="file" id="file1" name="file1" class="uploadify1" /></p> <p><input type="file" id="file2" name="file2" class="uploadify1" /></p> <p><input type="file" id="file3" name="file3" class="uploadify1" /></p> <p><button id="btn2">Upload Files</button></p>
The ‘button’ element is again used to start the upload processing. Each ‘input’ element has a unique ‘name’ attribute. If this were posted back in a normal HTML ‘form’ then this ‘name’ attribute would follow the file to the server where the file processing could be based upon the value of the ‘name’ attribute. Here is the JavaScript that configures this scenario:
// Multiple files - multiple inputs var packageId = 'multiple1' + new Date().getTime() + Math.round(Math.random() * 1000); $(".uploadify1").each(function () { $(this).uploadify({ 'uploader': '<%= Url.Content("~/Content/Scripts/uploadify/uploadify.swf") %>', 'script': '<%= Url.Action("Upload2") %>', 'fileDataName': 'file', 'buttonText': 'File Input...', 'multi': false, 'scriptData': { 'packageId': packageId, 'type': $(this).attr('name') }, 'sizeLimit': 1048576, 'simUploadLimit': 1, 'cancelImg': '<%= Url.Content("~/Content/Scripts/uploadify/cancel.png") %>', 'auto': false, 'onError': function (a, b, c, d) { if (d.status == 404) alert("Could not find upload script. Use a path relative to: " + "<?= getcwd() ?>"); else if (d.type === "HTTP") alert("error " + d.type + ": " + d.status); else if (d.type === "File Size") alert(c.name + " " + d.type + " Limit: " + Math.round(d.info / (1024 * 1024)) + "MB"); else alert("error " + d.type + ": " + d.text); }, 'onComplete': function (event, queueId, fileObj, response, data) { alert(response); } }); }); $("#btn2").click(function () { $(".uploadify1").uploadifyUpload(); });
In this case, each input element is configured to only allow a single file to be selected (‘multi’:false). The key configuration in this case is the ‘scriptData’ option. This options allows JSON data to be sent back to the server along with the file. In this case we send a ‘packageId’ (created on the first line) and a ‘type’. The ‘packageId’ is used to associate the files into groups on the server. The ‘type’ variable is the ‘input’ element’s ‘name’ attribute and allows the server side code to differentiate the files. The ASP.NET MVC controller now looks like the following:
public string Upload2(HttpPostedFileBase file, string packageId, string type) { // Add the file to the package. // Package package = PackageManager.GetPackage(packageId); package.AddFile(file, type); return "Upload received.nFilename: " + file.FileName + "nPackage Id: " + packageId + "nType: " + type; }
This method not only receives the file, but also the JSON data sent by Uploadify. The JSON data is then used to add the incoming file to a particular package. The specific implementation of the packaging system is not important (and not shown), but the key is that the server has the information necessary to implement the packaging feature. The above code could just as easily dispatched the file to various processors based upon the ‘type’ parameter.
The above works great for processing files based upon their types. The next challenge is processing files and form data where the form data provides important context for processing.
Multiple Files With Multiple HTML Input Plus Form Data
Assume we have an HTML ‘form’ that accepts multiple files and the first/last name of the user. The files must be processed based upon who uploaded the files. Therefore the first/last name data is important and required before processing can occur. Here is the HTML defining the form:
<form id='form1' method='post' action='<%= Url.Action("ProcessForm") %>'> <p><em>Provide your name:</em></p> <p>First Name <input type="text" name="firstName" id="fname" /></p> <p>Last Name <input type="text" name="lastName" id="lname" /></p> <p><em>Using the buttons below, navigate and select up to three files (1MB size limit) then click the 'Submit' button:</em></p> <p><input type="file" id="file4" name="file1" class="uploadify2" /></p> <p><input type="file" id="file5" name="file2" class="uploadify2" /></p> <p><input type="file" id="file6" name="file3" class="uploadify2" /></p> <p><input type="submit" id="btn3" /></p> </form>
Traditionally (without Uploadify involved) this form would POST all the data to a single ASP.NET MVC action method and no upload progress would be provided. Hooking up Uploadify provides the desired progress indicators, but now the files are uploaded ‘out of band’. Some special handling is necessary to keep the files and the form data associated. Here is the JavaScript that configures the form:
// Multiple files - multiple inputs in a form var packageId2 = 'multiple2' + new Date().getTime() + Math.round(Math.random() * 1000); var elem = $("<input type='hidden' name='packageId' value='" + packageId2 + "' />"); $("#form1").prepend(elem); $(".uploadify2").each(function () { $(this).uploadify({ 'uploader': '<%= Url.Content("~/Content/Scripts/uploadify/uploadify.swf") %>', 'script': '<%= Url.Action("Upload2") %>', 'fileDataName': 'file', 'buttonText': 'File Input...', 'multi': false, 'scriptData': { 'packageId': packageId2, 'type': $(this).attr('name') }, 'sizeLimit': 1048576, 'simUploadLimit': 1, 'cancelImg': '<%= Url.Content("~/Content/Scripts/uploadify/cancel.png") %>', 'auto': false, 'onError': function (a, b, c, d) { if (d.status == 404) alert("Could not find upload script. Use a path relative to: " + "<?= getcwd() ?>"); else if (d.type === "HTTP") alert("error " + d.type + ": " + d.status); else if (d.type === "File Size") alert(c.name + " " + d.type + " Limit: " + Math.round(d.info / (1024 * 1024)) + "MB"); else alert("error " + d.type + ": " + d.text); }, 'onComplete': function (event, queueId, fileObj, response, data) { incrementUploadCount(); } }); }); $("#btn3").click(function (evt) { evt.preventDefault(); submit(); });
The Uploadify elements are configured nearly the same as before using the ‘scriptData’ option to send back the ‘packageId’ and the ‘type’ information as JSON. However, in this case the ‘packageId’ is prepended to the form as an HTML hidden ‘input’ element (first three lines). In addition the Uploadify ‘onComplete’ callback now calls the ‘incrementUploadCount’ function (covered below) and the HTML submit button is wired up to call the ‘submit’ function.
var numFilesSelected = 0; var numFilesUploaded = 0; function submit() { // validate the form if ($('#fname').val() === '') { alert('Provide a first name.'); return; } if ($('#lname').val() === '') { alert('Provide a last name.'); return; } // determine the number of files that need to be // uploaded and reset the uploaded count numFilesSelected = $(".uploadifyQueueItem").length; numFilesUploaded = 0; // upload the files $('.uploadify2').uploadifyUpload(); } function incrementUploadCount() { numFilesUploaded++; if (numFilesUploaded === numFilesSelected) { alert('About to submit the form data.'); postFormData(); } } function postFormData() { $("#form1").submit(); }
The above code defines the client side pipeline that eventually upload the files and submit the form. The submit button calls the ‘submit’ function. In the ‘submit’ function, the form is first validated. Then the number of files being uploaded is determined and a counter is set to zero. Finally, the Uploadify elements are triggered.
Each Uploadify element calls the ‘incrementUploadCount’ upon successfully uploading the file to the server. Once all the files have been uploaded, the form is submitted. Here is the ASP.NET MVC action methods that are needed:
public string Upload2(HttpPostedFileBase file, string packageId, string type) { // Add the file to the package. // Package package = PackageManager.GetPackage(packageId); package.AddFile(file, type); return "Upload received.nFilename: " + file.FileName + "nPackage Id: " + packageId + "nType: " + type; } [AcceptVerbs(HttpVerbs.Post)] public ActionResult ProcessForm(string firstName, string lastName, string packageId) { Package package = PackageManager.GetPackage(packageId); package.FirstName = firstName; package.LastName = lastName; PackageManager.RemovePackage(package.Id); return View("UploadReceipt", package); }
The ‘Upload2’ action method is the same as before. It collects the uploaded files into a package. The ‘ProcessForm’ action method receives the rest of the form data along with the ‘packageId’. The ‘packageId’ is used to request the ‘Package’ that contains the associated files. The form data is then added to the ‘Package’. Once complete the ‘Package’ can be sent off for processing as a whole. In this case, it is simply sent to a ‘View’ to render a receipt page.
Summary
Uploading multiple files with progress using Uploadify is an option. Depending on your requirements there can be additional complexity. In this post, we have covered a way of accomplishing multiple file uploads under various requirements. As always, if you have feedback post a comment.
[ Update ]
Here is the package manager that I put together. I did not put a lot of thought or testing into this implementation.>
public class UploadedFile { public HttpPostedFileBase Stream { get; set; } public string Type { get; set; } } public class Package { public string Id { get; private set; } public List<UploadedFile> Files { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public Package(string id) { Id = id; Files = new List<UploadedFile>(); } public void AddFile(HttpPostedFileBase file, string type) { UploadedFile fileInfo = new UploadedFile { Stream = file, Type = type }; Files.Add(fileInfo); } } public static class PackageManager { private static readonly List<Package> _packages = new List<Package>(); public static Package GetPackage(string packageId) { Package package = _packages.Where(p => p.Id == packageId).SingleOrDefault(); if (package == null) { package = new Package(packageId); _packages.Add(package); } return package; } public static void RemovePackage(string packageId) { Package package = _packages.Where(p => p.Id == packageId).SingleOrDefault(); if (package != null) { _packages.Remove(package); } } }
Hi Bob
Nice post, just getting into MVC and using Uploadify. Looking at your code, can you provide some more insight on Package Manager and how you’re using it?
Regards
Michael.
Hi Michael,
Thanks for visiting the blog. Sorry about the delay. I have updated the post to include the PackageManger code that I was using.
Bob
Could you put here the source code (maybe an example of 1 html input with multiple file uploads plus form data).
I am trying to upload a file along with form data , the file gets uploaded put the action of the form doesnt execute , i cant even get in the increment function i dont know why.
I put all the client-side code in the page (javascript, css, html) https://bobcravens.com/Uploadify/Test/Multiple. Go there and view source. On that page are the ASP.NET MVC controllers that I am using. If you post your code (maybe to jsFiddle.net) I can help a bit more.
Bob
Problem solved , my upload Action was void , changed it to return string and the onComplete fires.
Hi,
I’m looking for a way for the upload folder to be determined by user input.
User enters an email address, then selects files to be uploaded. Once selection is made the click and upload to a newly created folder named from the MD5 of the email address.
Any help would be greatly appreciated!
All the best,
Derek
The post shows how to upload multiple files in a couple of different ways. The last example in the post (and the demo page) is probably close to what you want. Instead of ‘First Name’ and ‘Last Name’ substitute your ‘Email Address’ input. After all the files are uploaded (these are asynchronous) the form will be submitted. On the submit handler, you can do the work of creating your folder and saving the files. You should know that the MD5 hash does produce collisions. It may be at an acceptable rate for you. If this is public facing, you may want to use a different hash algorithm.
Hope this helps.
Bob
Hi Bob, your article help me so much.
I use it with Ajax function on ‘onComplete’ code.
Maybe you should put your code into the next release in uploadify page.
Regards.
Hi Bob,
Your article is really helpful.
just getting into ASP.Net and Jquery . Looking at your code, can you provide some more code and how you’re using it?
Hi Bob,
Your article is really helpful.I try many way before found ur article.
Thank so much
PS. Please do more and more the best article.
Can I get zip of this source code, I am getting difficulties
The source code is really just what is on the demo page (https://bobcravens.com/Uploadify/Test/Multiple). The demo page shows the exact controllers. If you right-click and view-source, you have the html/javascript. What are the difficulties that you are having?
Bob
Hello,
I was wondering about the importance of the ‘package’? I am trying to implement this but my problem when i upload nothing is uploaded. I tried debugging to see where the error is at and when i hit my controller i notice that the files count is always zero no matter how many file I’ve actually selected. Everything seems right but cannot really tell where the issue is. Are you willing to help if i post parts of my code for the upload?
Hi Amy,
The package system is used to keep the grouping of files together and then take some final action on the group. I am not certain of the exact issue that you are having. If you post some parts, I can try to take a look.
Bob
how about single html input with form data
Thanks for the post
how I can do it with razor mvc 3?
how I can do it with razor mvc 3 — > Multiple Files With Multiple HTML Input Plus Form Data
If inftamroion were soccer, this would be a goooooal!
Thank you very much! This article was really helpful!