TheDudeDude
TheDudeDude

Reputation: 123

ASP.NET MVC file download and jQuery parameter

I have a (ViewResult) Controller which receives a string parameter, generates a PDF file, and sends the PDF file back to the browser. I’ve tested the controller itself, and it works fine. Unfortunately, when I try to post to this controller from the $.ajax jQuery function (passing a simple string), the Controller always receives the string parameter as null. I've tried a hundred different configurations of the $.ajax function. Here's the controller, which returns a PDF to the browser (it works...as long as I create the HTML within the method):

[HttpPost]
public ActionResult HtmlToPdf(String htmlData)
{ }

Here's the jQuery I'm using in my view (triggered by a button click):

function getPdf() {
    var htmlData = “blah, blah, etc.”;
    $.ajax({
        url: '/Home/HtmlToPdf',
        type: 'post',
        data: JSON.stringify(htmlData),
        contentType: 'application/json; charset=utf-8',
        success: handleSuccess,
        error: handleError
    });
}

I've tried 'post', 'get', json, text, html, stringify, different content types, etc. Does anyone know how to correctly send a string (the var 'htmlData' above) to a controller? Post? Get? Something else? Thanks.

Upvotes: 2

Views: 20746

Answers (4)

StarNamer
StarNamer

Reputation: 700

Using the double POST method and a free jQuery extension 'fileDownload' from here

/*
* jQuery File Download Plugin v1.4.1
*
* http://www.johnculviner.com
*
* Copyright (c) 2013 - John Culviner
*
* Licensed under the MIT license:
*   http://www.opensource.org/licenses/mit-license.php
*/

Which basically wraps a POST in some code which helps handling some of the complexity...

We have (with large chunks removed (!)...

        $.ajax({
            url: emr.baseURL + '/Control/SaveData',
            type: 'POST',
            data: JSON.stringify(dataToPost),
            dataType: 'json',
            contentType: 'application/json; charset=utf-8',
            success: function (json) {
// stuff deleted which updates page
                        $.fileDownload(
                            '/Control/Export',
                            {
                                data: {
// data list deleted...
                                    ))
                                },
                                httpMethod: 'POST'
                            });
                    }
            },
            error: function () {
// deleted
            }
        });

With controller methods (cut down)

public JsonResult SaveData(/* ... */)
{
    /*
     ... do stuff ...
     */
    return /* JsonResult... */;
}

[HttpPost]
public void Export(/* ... */)
{
    /*
     ...
     */
    HttpContext context = System.Web.HttpContext.Current;
    /*
     ...
     */
    byte[] XLSXdata;
    /*
     ...Fill XLXSdata...
     */
    context.Response.Clear();
    context.Response.ClearHeaders();
    context.Response.ClearContent();
    context.Response.Buffer = false;
    context.Response.ContentType = "application/vnd.ms-excel";
    context.Response.AddHeader("content-disposition", "attachment;filename="+filename);
    context.Response.Charset = "";
    context.Response.BinaryWrite(XLSXdata);
    context.Response.Flush();
    context.Response.End();
}

Obviously, this is explicitly controlling what goes into the reponse.

Hope this helps!/

Upvotes: 0

StarNamer
StarNamer

Reputation: 700

You need to send it as a json object:

function getPdf() {
    var htmlData = “blah, blah, etc.”;
    var dataToPost = { htmlData: htmlData };
    $.ajax({
        url: '/Home/HtmlToPdf',
        type: 'POST',
        data: JSON.stringify(dataToPost),
        dataType: 'json',
        contentType: 'application/json; charset=utf-8',
        success: handleSuccess,
        error: handleError
    });
}

You then access in HomeController by just declaring it as an argument as you have done. The point is that you need to pass the name of the argument as part of the JSON object. The fact that you variable name is also 'htmlData' is irrelevant. The above could as easily be ...

var stuff = "blah, etc...";
var dataToPost = { htmlData: stuff };

Obviously for multiple arguments you just have more items in your object...

var dataToPost = { arg1: someData, arg2: 2, arg3: true }

... with ...

public ActionResult DoStuff(string arg1, int? arg2, bool? arg3) {}

If you want to pass an array add traditional:true to the $.ajax argument object.

I also note that I've always put 'POST' in upper case since, if you look at the documentation here the datatype: arguments (e.g. 'json') are listed in lower case, but 'GET' and 'POST' for type:are in upper case.

---------------------------- Update -------------------------

As mentioned in comments, for a simple file download, it's probably easier to use a GET.

The basic controller is something like (I'm trimming down code so haven't tested this)

public FileResult DownloadFile(string filename)
{
    byte[] fileContent = new byte[0];
    using(FileStream fs = File.OpenRead(filename)) {
        fileContent = new byte[fs.Length];
        fs.Read(fileContent, 0, Convert.ToInt32(fs.Length));
    }
    UTF8Encoding encoder = new UTF8Encoding();
    return File(encoder.GetBytes(fileContent), "applicaton/text", filename);
}

Then in the javascript do:

$.get('/Home/DownloadFile',
    function(data, textStatus, jqXHR) { /* don't think anything needs to be done */ }
).error(alert('Download failed'));

This obviously downloads a text file so you'd need to play with the encoding (possibly/probably don't need one?) and contentType string for PDF (I think it's just 'application/PDF'). Also, to repeat, I haven't tested this exact code, just edited the logic out of an existing application. Good luck.

Upvotes: 5

TheDudeDude
TheDudeDude

Reputation: 123

Thanks for the answers. I think all of them were correct, and good. I gave up on this one, though, because I think I was trying to do something that's not really possible: sending an ajax call to the server and having the server "finish the job" by returning a binary file to the browser (as a PDF). The ajax function was executing, getting a success message, and running its success function. Lost in all this was the fact that the browser was supposed to be receiving a file response, not an ajax response. Sorry if I'm not explaining it very well. Anyway, I dropped back and punted: I'm generating my file by rendering a .NET report (.rdlc) as a PDF, and sending that to the browser. Crude, but effective. In hindsight, I don't think ajax was even the way to go on this. :-p. Thanks again for the help.

Upvotes: 0

Alconja
Alconja

Reputation: 14873

Try sending your data as a json object and setting the dataType to 'json':

function getPdf() {
    var htmlData = “blah, blah, etc.”;
    $.ajax({
        url: '/Home/HtmlToPdf',
        type: 'post',
        data: { htmlData: htmlData },
        dataType: 'json',
        contentType: 'application/json; charset=utf-8',
        success: handleSuccess,
        error: handleError
    });
}

Upvotes: 2

Related Questions