Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Need help debugging XHR-based Ajax Image Upload with ASP.NET MVC2

I'm attempting to use the script found from http://valums.com/ajax-upload/

My controller is as follows

using System;
using System.IO;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Hosting;
using System.Web.Mvc;
using MHNHub.Areas.ViewModels;
using MHNHub.Models;
using MHNHub.ViewModels;

namespace MHNHub.Areas.Admin.Controllers
{
    [Authorize(Roles = "Administrator")]
    public class ImageController : Controller
    {

        private MHNHubEntities _entities = new MHNHubEntities();

        //
        // GET: /Image/
        [AcceptVerbs(HttpVerbs.Get)]
        public ActionResult ImageUploader()
        {
            var viewModel = new ImageViewModel()
            {
                Image = new Image()
            };

            return PartialView(viewModel);
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult ImageUploader(Image image)
        {
            try
            {
                _entities.Images.AddObject(image);
                _entities.SaveChanges();

                return RedirectToAction("Index", "Product");
            }
            catch (Exception ex)
            {
                var viewModel = new ImageViewModel()
                                {
                                    Image = image,
                                    HasError = true,
                                    ErrorMessage = ex.Message
                                };
                return PartialView(viewModel);

            }
        }

        private string _uploadsFolder = HostingEnvironment.MapPath("~/App_Data/Files");

        public Guid Upload(HttpPostedFileBase fileBase)
        {
            var identifier = Guid.NewGuid();
            fileBase.SaveAs(GetDiskLocation(identifier));
            return identifier;
        }

        private string GetDiskLocation(Guid identifier)
        {
            return Path.Combine(_uploadsFolder, identifier.ToString());
        }

    }

}

And I have a partial view like this

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MHNHub.ViewModels.ImageViewModel>" %>

<script type="text/javascript">
    $(function () {
        $("#imagedialog").dialog({
            bgiframe: true,
            height: 170,
            width: 430,
            modal: true,
            autoOpen: false,
            resizable: true
        })
    });

    $(document).ready(function createUploader() {
        var uploader = new qq.FileUploader({
            element: document.getElementById('fileuploader'),
            action: '/Image/Upload/',
            name: 'name'
        });

    });

</script>    

<div id="imagedialog" title="Upload Image">

                <div id="fileuploader">

                </div>
                <h6>Drag and drop files supported in Firefox and Google Chrome with javascript enabled.</h6> 
                    <noscript>
                         <form action="/image/upload" enctype="multipart/form-data" method="post">
                            Select a file: <input type="file" name="photo" id="photo" />   

                            <input type="submit" value="Upload" name="submit"/>
                        </form>
                    </noscript>


</div>

<div class="editor-field">
    <img src="<%: Model.Image.FileName %>" />
    <%: Html.TextBoxFor(model => model.Image.FileName) %>
    <%: Html.ValidationMessageFor(model => model.Image.FileName)%>
    <a href="#" onclick="jQuery('#imagedialog').dialog('open'); return false">Upload Image</a>
</div>

I have fileuploader.js and fileuploader.css linked properly on the master page, and the uploader appears correctly, and its even calling my action, but the HttpPostedFileBase is null and the upload action throws an exception. Any insight as to what I should do?

Edit

So I've figured out using firebug that its sending an XmlHttpRequest. How do I handle this in my upload action?

like image 615
Grahame A Avatar asked Sep 13 '10 03:09

Grahame A


1 Answers

The reason you are getting an empty parameter in your controller action is because this plugin doesn't send a multipart/form-data request to the server. Instead it sends application/octet-stream content type request header and it writes the file contents directly to the request stream, appending a parameter ?qqfile to the URL containing the file name. So if you want to retrieve this on the controller you will need to directly read the stream:

[HttpPost]
public ActionResult Upload(string qqfile)
{
    using (var reader = new BinaryReader(Request.InputStream))
    {
        // This will contain the uploaded file data and the qqfile the name
        byte[] file = reader.ReadBytes((int)Request.InputStream.Length);
    }
    return View();
}

If you select multiple files the plugin simply sends multiple requests to the server so this will work.

Also if you want to handle files bigger than int.MaxValue you will have to read from the request stream in chunks and write directly to an output stream instead of loading the whole file into a memory buffer:

using (var outputStream = File.Create(qqfile))
{
    const int chunkSize = 2 * 1024; // 2KB
    byte[] buffer = new byte[chunkSize];
    int bytesRead;
    while ((bytesRead = Request.InputStream.Read(buffer, 0, buffer.Length)) > 0)
    {
        outputStream.Write(buffer, 0, bytesRead);
    }
}

Remark: Remove the createUploader function name from your document.ready. It should be an anonymous function there. You could even merge it with the $(function() { ... }); you already have to setup the modal dialog.

like image 101
Darin Dimitrov Avatar answered Oct 12 '22 00:10

Darin Dimitrov