Sunday, 2 October 2011

Walkthrough example with NeatUpload and ASP.NET MVC 3

Through this post I’ll use an ASP.NET MVC 3 application but everything I show is possible using WebForms or whatever you want to use, let’s start. The source code for this code is available from here.
Setting up
Create an Empty ASP.NET MVC 3 Application by File | New Project | ASP.NET MVC 3 Application | Empty. I have chosen “MvcApplicationNeatUpload” as application name. Add the plugin .js file that you can download from here, in the folder “~/Scripts/” that comes with the project template or where you consider convenient.

Add Reference to the Brettle.Web.NeatUpload.dll file and the appropriated modifications to the root web.config file as mentioned in previous post. For example, the section configuration/system.webServer for my sample application contains this (In the sample I use IISExpress and it reads the configuration from that section, if you use the traditional WebDev you should change this as indicated in the previous post):

<modules runAllManagedModulesForAllRequests="true">
    <add name="UploadHttpModule" type="Brettle.Web.NeatUpload.UploadHttpModule, Brettle.Web.NeatUpload" preCondition="managedHandler"/>
</modules>
<security>
    <requestFiltering>
        <requestLimits maxAllowedContentLength="1000000000"/>
    </requestFiltering>
</security>

One last thing we have to use from original project (NeatUpload) is the file (ProgressJsonHandler.ashx) which can be found in the example I attach with this post. The propose of this file is provide an entry point for call the library methods that store in session the current file progress information and of course the default path in the plugin is that, nevertheless you can call them manually for catch that information but that’s out the scope of this post. I’ll cover more detailed information of the module in further posts.
Starting with the code
I assumed that you were using the jquery core files that come with the new project template; otherwise you can download it from (http://jquery.com). Now let’s create a controller and its corresponding view, for instance I have created the HomeController and the Index view.
This is the initial code for HomeController (~/Controllers /HomeController.cs):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Brettle.Web.NeatUpload;

namespace MvcApplicationNeatUpload.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    }
}

The controller doesn’t have yet anything relevant; it’s just an entry point for display the Index view. And code for the Index View (~/Views/Home/Index.cshtml):

@{
    ViewBag.Title = "Index";
}
<h2>Index</h2>
<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.neatupload.js")" type="text/javascript"></script>
<script type="text/javascript" defer="defer">
    $(function() {
        $("#file1").neatupload({
            postDataUrl: $("input[type=hidden][id=upload-action]").val(),
            fnShowStatus: function (plugin, index, data) {
                $('span#progress-file').text(data.PercentComplete + "%");
            }
        });
    });
</script>

@using(Html.BeginForm("UploadFile", "Home"))
{
    @Html.Hidden("upload-action", Url.Action("UploadFile"))
    <div>
        @Html.Label("newfield", "Select File: ")
        <div id="file1">
            place holder for the input -> file for upload
        </div>
    </div>
    <div>
        <span id="progress-file"></span>
    </div>
    <div>
        <input type="submit" value="save"/>
    </div>
}
In the case of the view, there are some elements to clarify. The scripts to include, the first is the version 1.5.1 minified of the jquery library (which comes by default with ASP.NET MVC 3) and the second is the jquery.neatupload.js plugin, it’s important the order because any jquery plugin must load after the jquery core. The $("#file1") refers to any element with an id equals to file1 (for jquery syntax go to jquery.com). The text inside the div with id = file1 is just for be sure the plugin is running properly, so you can remove it.

In this case the only options set are postDataUrl and fnShowStatus, the remaining options use the defaults defined by the plugin.
The postDataUrl option define the url that will handle the post on the server which is stored in a hidden field, the result of Url.Action("UploadFile") give us the resolved route for the Action UploadFile in the HomeController, the important note here is the strategy of not setting this value hardcoded in JavaScript code block but store it somewhere and by JavaScript code retrieve it. If we ignore this we can get a maintenance issue caused by refactoring or name changes in the actions or controllers.

The function fnShowStatus allows to the user to execute custom code each time the progress changes, this function takes three parameters: plugin, index, and data. The plugin parameter is a reference to the object container and through plugin.options object we can access to the options either defaults or custom values set by the plugin’s user. The index is the unique identifier auto generated by the plugin at time of being inserted in the hidden queue; this value may be used to establish a correspondence between the hidden form and the visual elements. The data parameter holds a JSON returned by the handler that is polled by intervals, this JSON has multiple properties but the most important is PercentComplete which give us the percent value (0 - 100) and may be used for display to the user.
The label refers to newfield because this is the name that the input takes when it’s generated. Due to security issues the input must be moved away when the user clicks the add button, and a new one is created for replace it, the best option would be change the value by code but it’s impossible, so the name is always the same.
If we run the example at this point, nothing important are going to happen but the view is rendered and we can see the form (styles and CSS are out of this post, just basic HTML) with the two links mentioned before.
What happen on the server?
Someone must receive the data posted by hidden forms when the user clicks add and then upload. Since we are using MVC the most appropriated place for this task is a Controller; however, you can choose a classic handler either a .ashx file or a class that implements IHttpHandler directly or even a Page. But back to reality, we are using MVC so let’s go to the HomeController and add the following action:


[HttpPost]
public ActionResult UploadFile(FormCollection collection)
{
      var tempPath = Server.MapPath("~/_temp_upload/");
      var value = string.Empty;
      for (var i = 0; i < UploadModule.Files.Count; i++)
      {
            value = UploadModule.Files[i].FileName;
            UploadModule.Files[i].SaveAs(tempPath + value);
      }
 
      return Content(value);
}

This action is responsible for receive the post data through the parameter collection and it’s annotated with [HttpPost] attribute for indicating that only is possible to execute this method if the request is a POST from a form. In the example we don’t do anything complex (it’s just sample) but to copy the uploaded content to a file with the same name in the hardcoded path “~/_temp_upload/” and return the name of the last file uploaded. Here, I am going to make a key point, due to the concrete implementation of the plugin in the client, the submits are made one by one, so there won’t be more than one file per upload, but it’s important to know that NeatUpload module is capable of handling more than one file per request.

The class UploadModule is our face for retrieve the uploaded content through the Files collection and the SaveAs method allows us to save in a particular path. At this point you are able to make whatever you want with the uploaded content, save to disk, create records in database or even start complex mechanisms that process these files like video encoding.
In the next post I’ll show more different plugin configurations and examples.

3 comments:

  1. Thanks for that post - nice to see, that neatUpload is working seamless and without problems in MVC :-)

    ReplyDelete
  2. Is that possible to have file extension restriction with neatUpload?

    ReplyDelete
    Replies
    1. Hi veasnamuch,
      I think you should take a look at this post http://abelperezmartinez.blogspot.co.uk/2012/02/jqueryneatupload-examples-refactored.html where I exposed serveral examples on how to use in different scenarios, specifically Validating Data.
      Thanks for reading,
      Abel.

      Delete