Question

I'm in the process of trying to get the jQuery plugin, Uploadify, to work with ASP.NET MVC.

I've got the plugin showing up fine with the following JavaScript snippet:

<script type="text/javascript">
    $(document).ready(function() {
        $('#fileUpload').fileUpload({
            'uploader': '/Content/Flash/uploader.swf',
            'script': '/Placement/Upload',
            'folder': '/uploads',
            'multi': 'true',
            'buttonText': 'Browse',
            'displayData': 'speed',
            'simUploadLimit': 2,
            'cancelImg': '/Content/Images/cancel.png'
        });
    });
</script>

Which seems like all is well in good. If you notice, the "script" attribute is set to my /Placement/Upload, which is my Placement Controller and my Upload Action.

The main problem is, I'm having difficulty getting this action to fire to receive the file. I've set a breakpoint on that action and when I select a file to upload, it isn't getting executed.

I've tried changing the method signature based off this article:

public string Upload(HttpPostedFileBase FileData)
{
    /*
    *
    * Do something with the FileData
    *
    */
    return "Upload OK!";
}

But this still doesn't fire.

Can anyone help me write and get the Upload controller action's signature correctly so it will actually fire? I can then handle dealing with the file data myself. I just need some help getting the method action to fire.

Was it helpful?

Solution

public string Upload(HttpPostedFileBase FileData) {}

is correct - the file uploaded by uploadify will get binded to FileData. No need to get into Request.Files to retrieve the file - which makes it harder to mock and test.

If your action isn't firing at all (i.e. try debugging and see if a breakpoint within the method is hit), then your issue is most likely the 'script' value - are you running under a virtual directory? If so you'll need to put the name of the directory in front. Uploadify is using an absolute path.

i.e. 'script: '/virtual_directory/Placement/Upload'

right now uploadify is sending to http://localhost/Placement/Upload.

also try using the route debugger (http://haacked.com/archive/2008/03/13/url-routing-debugger.aspx) to check where your route is being mapped to.

OTHER TIPS

The problem might be that you need to specify that the action you are uploading to is set to Post...it won't work with the action as a Get action.

So, this:

public string Upload(HttpPostedFileBase FileData)
{
   //do something
}

Should be this:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Upload(HttpPostedFileBase FileData)
{
   //do something
}

Also, be aware that if you are using this in a "logged in" section of your site, you should take a look here for a known bug with uploadify and authentication: http://geekswithblogs.net/apopovsky/archive/2009/05/06/working-around-flash-cookie-bug-in-asp.net-mvc.aspx

Also, just an aside, there are several ways in MVC to handle file uploads (using Request.Files as per Rory Fitzpatrick's suggestion, as well as passing the HttpPostedFileBase file as an argument in the action definition). It shouldn't really matter in order to get Uploadify to work.

This isn't how I had to implement file upload at all. I had an action method with no parameters that used the current Request object to dive into the collection of posted files.

Some sample code from my implementation:

[AcceptVerbs(HttpVerbs.Post)]
public ContentResult Upload() {
    if (Request.Files.Count > 0 && Request.Files[0].ContentLength > 0) {
        HttpPostedFileBase postedFile = Request.Files[0];
        // Do something with it
    }
}

Of course, writing tests for this becomes a PITA. You have to mock several objects in order to get it to work, for example:

var mockHttpContext = mocks.StrictMock<HttpContextBase>();
var mockRequest = mocks.StrictMock<HttpRequestBase>();
var postedFile = mocks.StrictMock<HttpPostedFileBase>();

var postedFileKeyCollection = mocks.StrictMock<HttpFileCollectionBase>();

mockHttpContext.Expect(x => x.Request).Return(mockRequest).Repeat.Any();
mockRequest.Expect(x => x.Files).Return(postedFileKeyCollection).Repeat.Any();

postedFileKeyCollection.Expect(x => x[0]).Return(postedFile).Repeat.Any();
postedFileKeyCollection.Expect(x => x.Count).Return(1);

postedFile.Expect(f => f.ContentLength).Return(1024);
postedFile.Expect(f => f.InputStream).Return(null);

It would be easier to create an interface into the posted files and only mock that, with a concrete implementation injected into your controller using IoC.

I think this was largely based off of this post: Implementing HTTP File Upload with ASP.NET MVC including Tests and Mocks

My full solution to this might solve your problem. Hope it helps.

http://zootfroot.blogspot.com/2010/12/mvc-file-upload-using-uploadify-with.html

Reading the documentation, it looks like it is sending an array of files. Have you tried:

public string Upload( HttpPostedFileBase[] fileData )

It's also possible that the default model binder won't work with HttpPostedFileBase and you'll either need to use Rory's mechanism or write your own model binder.

Here is my simple Razor View (The Layout master has the Javascript bundles)

@{
ViewBag.Title = "Upload Email CSV";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<script type="text/javascript" src="@Url.Content("~/Scripts/Uploadify/jquery.uploadify.js")"></script>

<script type="text/javascript">

$(function () {
    var uploadUrl = "@Url.Content("~/UploadFile/UploadEmailCSV/")";
    var uploadSWF = "@Url.Content("~/Scripts/Uploadify/uploadify.swf")";
    var uploadifyButtonImage = "@Url.Content("~/Scripts/Uploadify/uploadify.swf")";

    $('#file_upload').uploadify({
        'fileSizeLimit': '0',
        'buttonImage': '/uploadify/browse-btn.png',
        'swf': uploadSWF,
        'uploader': uploadUrl,
        'onUploadSuccess': function(file, data, response) {
            alert('The file was saved to: ' + data);
        }
    });
});
</script>
<h2>Upload a comma separated list of email addresses</h2>
@using (Html.BeginForm("UploadEmailCSV", "UploadFile", FormMethod.Post, new { @class = "form-horizontal", @enctype = "multipart/form-data", @id = "frmUploadFiles" }))
{
    <input type="file" name="file_upload" id="file_upload" />
}

Here is the Contoller Method

public ActionResult UploadEmailCSV()
    {
        var uploadedFile = Request.Files["Filedata"];

        if (uploadedFile != null && uploadedFile.ContentLength > 0)
        {
            var filePath = Path.Combine(Server.MapPath("~/UploadedFiles"), string.Format("{0}_{1}{2}", Path.GetFileNameWithoutExtension(uploadedFile.FileName), DateTime.Now.Ticks, Path.GetExtension(uploadedFile.FileName)));
            uploadedFile.SaveAs(filePath);
            return Content(string.Format("{0}_{1}{2}", Path.GetFileNameWithoutExtension(uploadedFile.FileName), DateTime.Now.Ticks, Path.GetExtension(uploadedFile.FileName)));

        }
        return Content("Error Uploading file!");
    }

Thats it!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top