5

I'm trying to pass an array/IEnumerable of Guids to a MVC 'GET' method that looks like this:

[HttpGet]
public ActionResult ZipResults(IEnumerable<Guid> ids)
{
    using(var zip = new Zip())
    {
        foreach(var id in ids)
        {
            var stream = GetDataStream(id);
            zip.AddEntry("filename.txt", stream);
        }
    }

    var outputStream = new MemoryStream();
    zip.Save(outputStream);

    return FileStreamResult(outputStream, "application/octet-stream"){
        FileDownloadName = "Results.zip" };
}

And my javascript looks like this:

$('the-button').click(function(){

    // 1. Get the guids from a table and add to javascript array (works fine)
    // 2. Grey-out screen and show processing indicator (works fine)

    // 3. This is how I'm calling the "ZipResults" action:
    $.ajax({
        url: '@Url.Action("ZipResults", "TheController")',
        type: 'GET',
        data: $.toJSON({ ids: _ids }),
        dataType: 'json',
        contentType: 'application/json;charset=utf-8',
        traditional: true,
        success: function(){
            // Undo the grey-out, and remove processing indicator
        },
        error: function(){
        }
    });
});

My expectation is that this will popup the download dialog on the browser. As it is, the javascript array being passed to the controller is null (on the server-side, it works correctly client-side). Also, this works fine with a 'POST', however, a 'POST' method used this way will not force the download dialog...

Open for suggestions :)

2 Answers 2

5

You should avoid sending JSON requests with GET. Try like this:

var _ids = [ 
    'e2845bd4-9b3c-4342-bdd5-caa992450cb9', 
    '566ddb9d-4337-4ed7-b1b3-51ff227ca96c',
    '25bc7095-a12b-4b30-aabe-1ee0ac199594'
];

$.ajax({
    url: '@Url.Action("ZipResults", "TheController")',
    type: 'GET',
    data: { ids: _ids },
    dataType: 'json',
    traditional: true,
    success: function() {
        // Undo the grey-out, and remove processing indicator
    },
    error: function() {

    }
});

This being said, I see that you are invoking some controller action which returns a file stream to download. You shouldn't be using AJAX at all to do this. The reason for that is that in your success callback you will get the contents of the ZIP file but there's not much you could do with it. You cannot save it to the client computer, you cannot prompt the user to choose a save location, you are pretty much busted.

So no AJAX calls if you want to download a file. You could use a simple anchor:

@Html.ActionLink("download zip", "ZipResults", "TheController", null, new { id = "download" })

and then:

$(function() {
    $('#download').click(function() {
        var _ids = [ 
            'e2845bd4-9b3c-4342-bdd5-caa992450cb9', 
            '566ddb9d-4337-4ed7-b1b3-51ff227ca96c',
            '25bc7095-a12b-4b30-aabe-1ee0ac199594'
        ];

        var url = this.href;
        for (var i = 0; i < _ids.length; i++) {
            if (url.indexOf('?') > 0) {
                url += '&ids=' + encodeURIComponent(_ids[i]);
            } else {
                url += '?ids=' + encodeURIComponent(_ids[i]);
            }
        }

        window.location.href = url;

        return false;
    });
});
Sign up to request clarification or add additional context in comments.

1 Comment

@ErOx, absolutely. Don't use any AJAX if you want to download files.
2

The problem is that a GET doesn't get a request body. I suspect that if you look at the client issued request, you would find that it is trying to shove an enumerable onto the query string....

Post -> Redirect seems like the best course of action here.

2 Comments

I was trying a post -> redirect before, but then passing the stream to the RedirectToAction gave me the error: "Timeouts are not supported on this stream."
Yep. To do this with a GET, I think you're going to need to send the data as query parameters rather than as a request body, then deal with the query params on the server side.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.