In our last few posts, we’ve been talking about HTML5 Drag and Drop. The feature lets uses drag elements on your page and drop them onto a target.
You can use this feature for users to upload files to your server.
In this post, you’ll learn:
- How you can provide HTML5 Drag and Drop
- How to send files using jQuery Ajax.
- Learn how to attach additional data onto the data sent to the sever
- Create a generic handler in ASP.NET to receive the files.
Of course other servers can be used, such as ASP.NET MVC, ASP.NET Web API, PHP, and Node.js. I’ll provide references to sample code for other servers in the References section.
Start Your Project
Let’s start. You can use Visual Studio 2012 or Visual Studio 2012 Express for Web for free.
Create an Empty ASP.NET Project in Visual Studio 2012.
Click File | New | New Website… the expand Templates, click Visual C#, click ASP.NET Empty Web Site in the center panel. Rename the Web Location to UploadFiles. Click OK.
Add jQuery to your project. Right-click your project, click Manage NuGet Packages… then search online for jQuery. Click Install on the jQuery item.
Click Close.
HTML5 Client
I wanted to demonstrate how you can create an HTML client that could be migrated to work on a device, not necessarily hosted by a Web server. To do this, we’ll create the client in an html file.
To create your Drag and Drop Client, right-click the project, click Add | Add New Item… then in Search Installed Templates input box, search for html page. Click HTML Page and change the name to UploadFiles.html, click Add.
Sample Code
In our example, the user is presented with a drop target in gray. When the user drags items onto the target, the target color becomes light yellow. When the user drops the files, the drop target text tells how many files were dropped. The user clicks the Send Files button to send the files to the server.
There are many other ways you can provide this functionality. This example shows one way that you can customize as needed.
So, replace the code in the HTML page with the following:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<title>Drop files on me</title> | |
<style type="text/css"> | |
#dropOnMe { | |
width: 210px; | |
height: 136px; | |
padding: 10px; | |
border: 2px dashed gray; | |
background-color: lightgray; | |
} | |
</style> | |
<script src="Scripts/jquery-1.9.1.js"></script> | |
<script> | |
$(document).ready(function () { | |
// this function runs when the page loads to set up the drop area | |
// Check if window.fileReader exists to make sure the browser | |
// supports file uploads | |
if (typeof (window.FileReader) == 'undefined') { | |
alert('Browser does not support HTML5 file uploads!'); | |
} | |
dropOnMe.addEventListener("drop", dropHandler, false); | |
dropOnMe.addEventListener("dragover", function (ev) { | |
$("#dropOnMe").css("background-color", "lightgoldenrodyellow;"); | |
ev.preventDefault(); | |
}, false); | |
function dropHandler(ev) { | |
// Prevent default processing. | |
ev.preventDefault(); | |
// Get the file(s) that are dropped. | |
var filelist = ev.dataTransfer.files; | |
if (!filelist) return; // if null, exit now | |
$("#dropOnMe").text(filelist.length + | |
" file(s) selected for uploading!"); | |
$("#upload").click(function () { | |
var data = new FormData(); | |
for (var i = 0; i < filelist.length; i++) { | |
data.append(filelist[i].name, filelist[i]); | |
} | |
$.ajax({ | |
type: "POST", | |
url: "FileHandler.ashx", | |
contentType: false, | |
processData: false, | |
data: data, | |
success: function (result) { | |
alert(result); | |
}, | |
error: function () { | |
alert("There was error uploading files!"); | |
} | |
}); | |
}); | |
} | |
dropOnMe.addEventListener("dragend", function (ev) { | |
$("#dropOnMe").css("background-color", "lightgray;"); | |
$("#dropOnMe").text(""); | |
$("upload").click(function () { }); | |
ev.preventDefault(); | |
}, false); | |
}); | |
</script> | |
</head> | |
<body> | |
<h3>Drop Files on Me</h3> | |
<div id="dropOnMe" draggable="false"></div> | |
<div id="fileCount" draggable="false"></div> | |
<input id="upload" draggable="false" type="button" | |
value="Upload Selected Files" /> | |
<div draggable="false"> | |
<ol draggable="false" id="myFileList"></ol> | |
</div> | |
</body> | |
</html> |
Now for the code walkthrough.
HTML 5 Drag and Drop Sample Code Walkthrough
First we added support for jQuery.
<script src="Scripts/jquery-1.9.1.js"></script>
Once the document is ready, check to be sure that the browser supports the ability to upload files using XmlHttpRequest 2. You can do that with the following code:
if (typeof(window.FileReader) == 'undefined') { alert('Browser does not support HTML5 file uploads!'); }
Next, we add the eventListeners for drop and dragover events. They are the same as we used in the previous post. Although we did add one small addition, because we have jQuery support. This time when the user drags an item into the target, the background target is turned light yellow.
dropOnMe.addEventListener("dragover", function (ev) { $("#dropOnMe").css("background-color", "lightgoldenrodyellow;"); ev.preventDefault(); }, false);
And we added an eventListener for dragend event that turns the color back to light gray, removed the text from the drop target, and remove the effect of click event on the upload button. when the drag has finished.
dropOnMe.addEventListener("dragend", function (ev) { $("#dropOnMe").css("background-color", "lightgray;"); $("#dropOnMe").text(""); $("upload").click(function () { }); ev.preventDefault(); }, false);
The drop handler begins the same as before in preventing event propogation, and getting the file list. And then we get a count of the files that are dropped and set the number into the text of the drop target.
// Prevent default processing. ev.preventDefault(); // Get the file(s) that are dropped. var filelist = ev.dataTransfer.files; if (!filelist) return; // if null, exit now $("#dropOnMe").text(filelist.length + " file(s) selected for uploading!");
Then we set up the upload click event handler. You add the file name to each file in the filelist. We’ll explain more about FormData in the next section.
var data = new FormData(); for (var i = 0; i < filelist.length; i++) { data.append(filelist[i].name, filelist[i]); }
Finally, we use the jQuery.ajax() function to send an asynchronous HTTP (Ajax) request as a POST to the url FileHandler.aspx that we’ll write in a following section.
We also add the FormData that where we add the filename to the payload. If the file is successful or comes back with an error, you can put up an.
Notice that the processData option is set to false. By default when you use the $.ajax() method the data is sent in URL encoded format. To prevent this behavior processData is set to false.
$.ajax({ type: "POST", url: "FileHandler.ashx", contentType: false, processData: false, data: data, success: function (result) { alert(result); }, error: function () { alert("There was error uploading files!"); } });
You can run the application now. But you will receive the error alert.
For more information about jQuery.ajax method, see jQuery documentation Ajax/jQuery.post.
About FormData
FormData objects provide a way to easily construct a set of key/value pairs representing form fields and their values that can be sent using the XMLHttpRequest send() method, which is what jQuery uses to send its data.
You add one or more key values to the data you are sending using formData.append(name, value). name is a string; value can be a string or blob. Each time the append is invoked, the browser creates a new entry with the name/value to the end of the collection of the formData instance.
FormData is supported in Chrome 7+, Firefox 4+, IE 10+ and Windows Store apps, Opera 12+, and Safari 5+.
Receiving the Files
There are a lot of ways you can receive files on the server — PHP, ASP.NET MVC, ASP.NET Web API, Node.js.
In this example, I’ve chosen to use a generic handler. It’s straight forward solution and provides you with completely disconnecting the html from the file server. This is useful for you to eventually move your HTML to another platform, such as smartphone or Windows Store.
Set Up The Generic Handler
To create a place to save the files, add a empty folder to your project named uploads. To do this, right-click project, click Add | New Folder. Rename the folder uploads.
Add a Generic Handler to the project named FileHandler.ashx. To do this, right-click your project, click Add | Add New Item… to see the Add New Item dialog. Next type generic in Search Installed Templates box.
Click Generic Handler in the center panel, type in the file name FileHandler.ashx and click Add.
Replace the code with the following.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%@ WebHandler Language="C#" Class="FileHandler" %> | |
using System; | |
using System.Web; | |
public class FileHandler : IHttpHandler | |
{ | |
public void ProcessRequest(HttpContext context) | |
{ | |
if (context.Request.Files.Count > 0) | |
{ | |
HttpFileCollection files = context.Request.Files; | |
foreach (string key in files) | |
{ | |
HttpPostedFile file = files[key]; | |
//string fileName = file.FileName; | |
//fileName = context.Server.MapPath("~/uploads/" + fileName); | |
string fileName = context.Server.MapPath("~/uploads/" + key); | |
file.SaveAs(fileName); | |
} | |
} | |
context.Response.ContentType = "text/plain"; | |
context.Response.Write("File(s) uploaded successfully!"); | |
} | |
public bool IsReusable | |
{ | |
get { return false; } | |
} | |
} |
Server Code Walkthrough
The ProcessRequest() method receives the files sent by the $.ajax() function on the server.and saves them to a folder on the server.
The uploaded file information is accessed using the Files collection of the Request object.
HttpFileCollection files = context.Request.Files;
A foreach loop iterates through all the files from the Request.Files collection and saves the individual file using the SaveAs() method of HttpPostedFile class. We use key that was passed in from the client’s FormData because it holds the name of the file.
foreach (string key in files) { HttpPostedFile file = files[key]; string fileName = context.Server.MapPath("~/uploads/" + key); file.SaveAs(fileName); }
Finally, we compose the message to be sent back to the jQuery.ajax command.
context.Response.ContentType = "text/plain"; context.Response.Write("File(s) uploaded successfully!");
You should still add some basic error handling, check to see if the file exists, add user identification on client and server using FormData, and much more.
Update Web.Config
You may wish to update the <httpRuntime> element your Web.Config file. For example, to limit a denial of service attack, posting large files to the server is limited. The default is 4 MB.
To update it to something like 20000 bytes:
<configuration> <system.web> <compilation debug="true" targetFramework="4.5"/> <httpRuntime targetFramework="4.5" maxRequestLength="20000" /> </system.web> </configuration>
References
This article is based on the code in Uploading Files Using HTML5 Drag-and-Drop and ASP.NET on CodeGuru.
jQuery Ajax documentation.
Using FormData Objects from Mozilla Developer Network.
Other Servers
- To receive files in node.js on Amazon S3, see LearnBoost/knox
- Example for a PHP server: HTML5 Drag and Drop Multiple File Upload
- Good example on developer.com Using HTML5 Drag and Drop in ASP.NET shows how to use a WebMethod in ASP.NET to receive the file.
- A guide to asynchronous file uploads in ASP.NET Web API RTM has source published on GitHub at WebApi.Html5.Upload
- Peter De Rycke wrote Implementing HTML5 Drag & Drop Based File Upload in ASP.NET MVC 3. The client code is uses Filedrop, but his server will work great for receiving files in MVC.
NOTE: jQuery plugin Filedrop appears in some of the ASP.NET examples. And it is has a fantastic JavaScript API for doing drag and drop. Unfortunately, Filedrop uses a feature that had been deprecated in W3C FileReader spec and so the client code is incompatible with IE and Windows Store apps.
Sample Code
Sample code is available in the DevDays GitHub repository. See https://github.com/devdays/html5-tutorials