Search code examples
jqueryajaxasp.net-mvcfilecontentresult

ASP.NET MVC 4 FileContentResult runs twice


I'm currently trying to view images that are stored in a database as BLOBs, to do that I've created a Controller Action of the type FileContentResult which looks like this:

public FileContentResult ImageFile(string imageId)
    {
        var client = new WcfTestClient();

        DataTable table = client.GetImageData(imageId);
        var image = ConvertToBytes(table);
        byte[] bytes;

        bytes = image[0].Values.ElementAt(0);

        return File(bytes, @"image/png");
    }

It gets the correct image data from the database and ConvertToBytes() works correctly as well but for some reason, after returning the image to the View it jumps back to the top and runs again.

I'm using Ajax to call the method from my View like this:

$.ajax({
    url: "/Home/ImageFile",
    type: "GET",
    data: { "imageId": imageId },
    success: function (data) {
        var image = document.createElement('img');
        image.src = "/Home/ImageFile?id=" + imageId;
        image.width = 100;
        image.height = 100;
        div.appendChild(image);
    }
});

Does anyone know what might cause my problem?

Update

Okay, so now after being told that it was Ajax that messed it up I've tried to send a parameter to my Controller Action like this:

var images = JSON.parse(window.localStorage.getItem("JsonImages"));
var div = document.getElementById('imgOutput');

for (var i = 0; i < images.length; i++) {
    var imageId = images[i].pi_Id;
    var imgId = JSON.parse('{"imageId":' + imageId + '}');

    var img = document.createElement('img');
    img.src = "/Home/ImageFile?id=" + imgId;
    img.width = 100;
    img.height = 100;
    div.appendChild(img);
}

Which, sadly, doesn't work very well. So, is there a way to send a parameter to ImageFile() without making it run twice? Am I missing something fundamental here?

Update 2

Finally I got it to work! This is how it looks now:

var images = JSON.parse(window.localStorage.getItem("JsonImages"));
var div = document.getElementById('imgOutput');

var createImage = function (src) {
    var img = document.createElement('img');
    img.src = src;
    img.height = 100;
    img.width = 100;
    return img;
}

for (var i = 0; i < images.length; i++) {
    var imageId = images[i].pi_Id;
    var imageSrc = "/Home/ImageFile?imageId=" + imageId;
    div.appendChild(createImage(imageSrc));
}

I just needed to change the source's parameter from id to imageId (duh). Thank you for all your help!


Solution

  • The problem is that you first issue an AJAX request to the ImageFile controller action, then, when it returns the result successfully, you create a DOM element which references that same route, so the browser issues another request in its attempt to create the image tag.

    You don't need that AJAX call at all, by the looks of it. If you simply create the image tag with the route of the action as the src, it should work. Pull your success callback out of the AJAX request and just use that on its own.

    Alternatively, if you're not doing anything particularly dynamic, you can just use the route directly in your image src tag and not use Javascript at all. For example, if you're working with Razor:

    <image src="@(String.Format("/Home/ImageFile?id={0}", Model.MyImage))"
        height="100" width="100" />
    

    If it needs to be dynamic and Javascript based, you can just drop the AJAX, because you don't need it:

    function createImage(src, width, height) {
        var img = document.createElement('img');
        img.src = src;
        img.height = height;
        img.width = width;
        return img;
    }
    
    var someId = /* Whatever your ID is */;
    var imgSrc = "/Home/ImageFile?id=" + someId;
    div.appendChild(createImage(imgSrc, 100, 100));