Uploading files asynchronously in asp.net mvc site

Hi,
I've been looking for quite some time for a complete solution to uploading files in ajax for my site.
For those of you who don't know, browsers won't let you upload files without making a full page reload.
I haven't been able to find a good, working and cross browser solution for this problem.
So after much research and development I have finally managed to solve this problem.

Lets take a look at the following controller.
public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public ActionResult UploadImage(HttpPostedFileBase image)
    {
        try
        {
            if (Request.ContentLength > 1024 * 1024 * 2)
            {
                return UploadCompanyLogoResult(new { success = false, error = "File size too big. Maximum file size is 2MB." });
            }

            if (Request.Files.Count == 0)
            {
                return UploadCompanyLogoResult(new { success = false, error = "Failed uploading file" });
            }

            image = Request.Files[0];
            var webImage = new WebImage(image.InputStream) { FileName = image.FileName };
            var imageUrl = SaveImageToFile(webImage);

            return UploadCompanyLogoResult(new { success = true, url = imageUrl });
        }
        catch (Exception e)
        {
            Logger.Instance.Error("Failed to upload image", LOG_CATEGORY, e.ToString());
            return UploadCompanyLogoResult(new { success = false, error = "Unknown error uploading file" });
        }
    }

    private string SaveImageToFile(WebImage webImage)
    {
        var filePath = Server.MapPath("~/Content/" + webImage.FileName);
        System.IO.File.WriteAllBytes(filePath, webImage.GetBytes());
        return Url.Content("Content/" + webImage.FileName);
    }

    private ActionResult UploadCompanyLogoResult(object json)
    {
        if (Request.UserAgent.ToLower().Contains("ie"))
        {
            var javascriptSerializer = new JavaScriptSerializer();
            return Content(string.Format("<pre>{0}</pre>", javascriptSerializer.Serialize(json)));
        }

        return Json(json);
    }
}
The controller has 2 actions:
1. Home
2. UploadImage
The Home action returns a simple view, that you'll see down below, and the UploadImage action is responsible for handling the file uploaded.
This is Index.cshtml:
For comfortable view I've written the css & js inside, but of course it's recommended to split them into different files.
The layout file contains only a reference to jQuery.
<style type="text/css">
    #container { position: relative; display: inline-block; }
    #container > .loading-circle-container { position: absolute; width: 100%; height: 100%; top: 0; left: 0; text-align: center; background: white; opacity: 0.9; display: none; }
    #container > .loading-circle-container > .loading_circle { display: inline-block; position: static; background: url('https://pictures.reuters.com/ClientFiles/RTR/Images/ajax-loader.gif') no-repeat; width: 37px; height: 37px; }
    #container > form { display: inline-block; text-align: center; }
    #container > form > label { display: block; cursor: pointer; }
    #container > form > label > #image { cursor: pointer; max-height: 150px; vertical-align: middle; }
    #container > form > #imageUpload { position: absolute; left: -9999em; }
</style>
<div id="container">
    @using (Html.BeginForm("UploadImage", "Home", FormMethod.Post, new { enctype = "multipart/form-data", target = "upload_file_target" }))
    {
        var defaultImage = "https://www.compuchecks.com/BJA/images/logo-placeholder.gif";
        <label for="imageUpload" id="button">
            <img id="image" src="@defaultImage">
        </label>
        <input type="file" name="imageUpload" id="imageUpload" />
    }
    <div class="loading-circle-container">
        <div class="loading_circle">
        </div>
    </div>
</div>
<script type="text/javascript">
    var uploadFileAjax = function (inputTag, successCallback) {
        var iframe = $('#upload_file_target');
        if (iframe.length == 0) {
            iframe = $('<iframe>').attr({
                id: 'upload_file_target',
                name: 'upload_file_target'
            }).css({
                width: 0,
                height: 0,
                border: 0
            }).appendTo($('body'));
        }

        iframe.load(function () {
            var iframeContents = iframe[0].contentWindow.document.body.innerHTML;
            successCallback($(iframeContents).html());
        });

        inputTag.parent().submit();
    }

    var container = $('#container');
    var loader = $('.loading-circle-container', container);
    var image = $('#image', container).load(function () {
        loader.fadeOut();
    });
    var imageUpload = $('#imageUpload', container).on('change', function () {
        uploadFileAjax(imageUpload, function (responseText) {
            var responseJson = JSON.parse(responseText);
            if (responseJson.success) {
                image.attr('src', responseJson.url);
            }
        });
    });

    $('form', container).on('submit', function () {
        loader.fadeIn();
    });

    if ($.browser.mozilla) {
        $('#button', container).click(function () {
            imageUpload.click();
        });
    }
</script>
You can test the code and see that it actually works (the page doesn't refresh - and the image uploads successfully).

Comments

Popular posts from this blog

Impersonating CurrentUser from SYSTEM

Add Styles & Scripts Dynamically to head tag in ASP .NET MVC

Json To Dictionary generic model binder