using Plupload with ASP.NET/C#

asp.net, c#, httphandler, jquery-plugins, plupload

Solution

Here's a full working example I wrote for you:

<%@ Page Title="Home Page" Language="C#" %>
<%@ Import Namespace="System.IO" %>
<script runat="server" type="text/c#">
    protected void Page_Load(object sender, EventArgs e)
    {
        // Check to see whether there are uploaded files to process them
        if (Request.Files.Count > 0)
        {
            int chunk = Request["chunk"] != null ? int.Parse(Request["chunk"]) : 0;
            string fileName = Request["name"] != null ? Request["name"] : string.Empty;

            HttpPostedFile fileUpload = Request.Files[0];

            var uploadPath = Server.MapPath("~/TicketUploads");
            using (var fs = new FileStream(Path.Combine(uploadPath, fileName), chunk == 0 ? FileMode.Create : FileMode.Append))
            {
                var buffer = new byte[fileUpload.InputStream.Length];
                fileUpload.InputStream.Read(buffer, 0, buffer.Length);

                fs.Write(buffer, 0, buffer.Length);
            }
        }
    }
</script>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head id="Head1" runat="server">
    <title></title>

    <style type="text/css">@import url(css/plupload.queue.css);</style>
    <script type="text/javascript" src="http://www.google.com/jsapi"></script>
    <script type="text/javascript">
        google.load("jquery", "1.3");
    </script>
    <script type="text/javascript" src="/plupload/js/gears_init.js"></script>
    <script type="text/javascript" src="http://bp.yahooapis.com/2.4.21/browserplus-min.js"></script>
    <script type="text/javascript" src="/plupload/js/plupload.full.min.js"></script>
    <script type="text/javascript" src="/plupload/js/jquery.plupload.queue.min.js"></script>
    <script type="text/javascript">
    $(function() {
        $("#uploader").pluploadQueue({
            // General settings
            runtimes : 'gears,flash,silverlight,browserplus,html5',
            url : '/default.aspx',
            max_file_size : '10mb',
            chunk_size : '1mb',
            unique_names : true,
            // Resize images on clientside if we can
            resize : {width : 320, height : 240, quality : 90},
            // Specify what files to browse for
            filters : [
                {title : "Image files", extensions : "jpg,gif,png"},
                {title : "Zip files", extensions : "zip"}
            ],
            // Flash settings
            flash_swf_url : '/plupload/js/plupload.flash.swf',
            // Silverlight settings
            silverlight_xap_url : '/plupload/js/plupload.silverlight.xap'
        });

        // Client side form validation
        $('form').submit(function(e) {
            var uploader = $('#uploader').pluploadQueue();
            // Validate number of uploaded files
            if (uploader.total.uploaded == 0) {
                // Files in queue upload them first
                if (uploader.files.length > 0) {
                    // When all files are uploaded submit form
                    uploader.bind('UploadProgress', function() {
                        if (uploader.total.uploaded == uploader.files.length)
                            $('form').submit();
                    });
                    uploader.start();
                } else
                    alert('You must at least upload one file.');
                e.preventDefault();
            }
        });
    });
    </script>

</head>
<body>
    <form id="Form1" runat="server">
        <div id="uploader">
            <p>You browser doesn't have Flash, Silverlight, Gears, BrowserPlus or HTML5 support.</p>
        </div>
    </form>
</body>
</html>

As you will see in this example files are uploaded to the same page called `default.aspx`. Notice that parameters such as `chunk` and `name` are POSTed so you shouldn't use `Request.QueryString` to read them but `Request["chunk"]` directly as this will look at the POST body as well. You should also make sure that the `TicketUploads` folder exists on the server at the root.

In this example the same page default.aspx is used for showing the upload form and handling the uploads. In a real world application this is not something I would do. I would recommend you using a separate script which will handle the file uploads such as a generic http handler (upload.ashx).

Finally you will notice that I have used some default settings that you might wish to modify and reconfigure the plugin to fit your needs. I just took the settings from the documentation.

UPDATE:

And since I recommended using a separate generic http handler for handling the file uploads here's how it might look :

using System.IO;
using System.Web;

public class Upload : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        int chunk = context.Request["chunk"] != null ? int.Parse(context.Request["chunk"]) : 0;
        string fileName = context.Request["name"] != null ? context.Request["name"] : string.Empty;

        HttpPostedFile fileUpload = context.Request.Files[0];

        var uploadPath = context.Server.MapPath("~/TicketUploads");
        using (var fs = new FileStream(Path.Combine(uploadPath, fileName), chunk == 0 ? FileMode.Create : FileMode.Append))
        {
            var buffer = new byte[fileUpload.InputStream.Length];
            fileUpload.InputStream.Read(buffer, 0, buffer.Length);

            fs.Write(buffer, 0, buffer.Length);
        }

        context.Response.ContentType = "text/plain";
        context.Response.Write("Success");
    }

    public bool IsReusable
    {
        get { return false; }
    }
}

Now all that's left is to reconfigure the plugin to point to this generic handler:

...
runtimes: 'gears,flash,silverlight,browserplus,html5',
url: '/upload.ashx',
max_file_size: '10mb',
...

Problem

UPDATE I was able to get everything to work properly and I just wanted to post back with the updated code. I used Darin Dimitrov's suggestion on using a separate generic http handler for handling the file uploads and so this is the code I came up with for that... let me know if you have questions. ``` <%@ WebHandler Language="C#" Class="Upload" %> using System; using System.Configuration; using System.Data; using System.Data.SqlClient; using System.IO; using System.Net; using System.Web; public class Upload : IHttpHandler { public void ProcessRequest(HttpContext context) { /** * If 'newTicket' is "false", then the directory to upload to already exists and we can extract it from * the 'ticketID' POST parameter. * * If 'newTicket' is "true", then this is a new Ticket submission so we need to work with a NEW directory * on the server, so the ID needs to be 1 more than the total number of directories in ~/TicketUploads/ */ String newTicket = context.Request["newTicket"] != null ? context.Request["newTicket"] : String.Empty; int theID = -1; if (newTicket.Equals("true")) { // we need to calculate a new ID theID = getNewID(context); // calculate the new ID = # of rows theID++; // add 1 to make it unique } else if (newTicket.Equals("false")) { // we can just get the ID from the POST parameter theID = context.Request["ticketID"] != null ? Convert.ToInt32(context.Request["ticketID"]) : -1; } else { // something went wrong with the 'newTicket' POST parameter context.Response.ContentType = "text/plain"; context.Response.Write("Error with 'newTicket' POST parameter."); } // if theID is negative, something went wrong... can't continue if (theID < 0) { return; } // ready to read the files being uploaded and upload them to the correct directory int chunk = context.Request["chunk"] != null ? int.Parse(context.Request["chunk"]) : 0; string fileName = context.Request["name"] != null ? context.Request["name"] : string.Empty; var uploadPath = context.Server.MapPath("~/TicketUploads/" + theID + "/"); HttpPostedFile fileUpload = context.Request.Files[0]; // if the NEW directory doesn't exist, create it DirectoryInfo di = new DirectoryInfo("" + uploadPath + ""); if (!(di.Exists)) { di.Create(); } using (var fs = new FileStream(Path.Combine(uploadPath, fileName), chunk == 0 ? FileMode.Create : FileMode.Append)) { var buffer = new byte[fileUpload.InputStream.Length]; fileUpload.InputStream.Read(buffer, 0, buffer.Length); fs.Write(buffer, 0, buffer.Length); } context.Response.ContentType = "text/plain"; context.Response.Write("File uploaded."); return; } } ``` I'm trying to integrate the Plupload file uploader in ASP.NET using C#. I've read the Angry Monkeys article as well as the Marco Valsecchi blog post but I'm a little lost. The C# that the above articles suggest is roughly similar to the following: ``` int chunk = Request.QueryString["chunk"] != null ? int.Parse(Request.QueryString["chunk"]) : 0; string fileName = Request.QueryString["name"] != null ? Request.QueryString["name"] : string.Empty; HttpPostedFile fileUpload = Request.Files[0]; using (FileStream fs = new FileStream(Server.MapPath("~/TicketUploads/" + fileName), chunk == 0 ? FileMode.Create : FileMode.Append)) { Byte[] buffer = new Byte[fileUpload.InputStream.Length]; fileUpload.InputStream.Read(buffer, 0, buffer.Length); fs.Write(buffer, 0, buffer.Length); fs.Close(); } ``` First, I have set up the Plupload configuration as follows: ``` $("#plupload_container").pluploadQueue({ runtimes: 'html5,gears,flash,silverlight,html4', flash_swf_url: '../plupload/js/plupload.flash.swf', silverlight_xap_url: '../plupload/js/plupload.silverlight.xap', filters: [ { title: "Image files", extensions: "jpg,gif" }, { title: "Zip files", extensions: "zip" }, { title: "Document files", extensions: "doc,pdf,txt" } ] }); ``` ... but I feel like I'm missing something here that will be necessary for the uploading to work. I guess my main question is how do I call the above C# code so that the uploading can begin? I have a form on a page named `SubmitRequest.aspx`. Clicking 'Submit' on the form results in the following: ``` $('form').submit(function (e) { // Validate number of uploaded files if (uploader.total.uploaded == 0) { // Files in queue upload them first if (uploader.files.length > 0) { // When all files are uploaded submit form uploader.bind('UploadProgress', function () { if (uploader.total.uploaded == uploader.files.length) $('form').submit(); }); uploader.start(); } e.preventDefault(); } }); ``` ... so the uploader starts when 'Submit' is clicked and uploads the files. Once that is done, the rest of the form is submitted. I don't understand how to link this event to the C# code that will handle the uploading to a folder `TicketUploads` on the server. I apologize for the longish post, but I would appreciate any help :)

Original source