开发者

POST to server, receive PDF, deliver to user w/ jQuery

I have a link that the user clicks to get a PDF. In jQuery, I create a POST ajax call to the server to get the PDF. The PDF comes to me with the correct content headers etc that would normally cause the browser to open the Reader plugin, or allow the user to save the PDF.

Since I am getting the PDF w/ an ajax call, I'm not sure what to do with the data that I get in the OnSucce开发者_如何学编程ss callback. How can I give the data I receive to the browser and allow it to do its default thing with the PDF response?


Take a look at - jQuery Plugin for Requesting Ajax-like File Downloads

The whole plugin is just about 30 lines of code (including comments).

The call is fairly similar to jquery ajax call.

$.download('/export.php','filename=myPDF&format=pdf&content=' + pdfData );

Ofcourse, you have to set the content-type and Content-Disposition headers on the server side as you would for any such download.

In java I would do something like this

response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename="exported.pdf");


You don't need jQuery at all. Just submit your POST via a form normally, and on the server side, add the HTTP header

Content-Disposition: attachment; filename="whatever.pdf"

The browser will do its default thing.

Alternately, if you want to be more careful about reporting any errors that might occur during the PDF generation, you can do this. POST your parameters to your server with jQuery. On the server, generate the binary content and cache it somewhere for a few minutes, accessible via a key that you put in the user's session, and return a "success" Ajax response to your page (or if there was an error, return an "error" response). If the page gets back a success response, it can immediately do something like:

window.location = "/get/my/pdf";

The server then returns the cached PDF content. Be sure to include the Content-Disposition header, as above.


The answer mentioning "jQuery Plugin for Requesting Ajax-like File Downloads" got me headed down the right direction, but it didn't work entirely for my situation since I have a complex object and array of objects to pass in as my search criteria/filter data. I figured I'd share my code in case someone else runs into this situation too.

$.download = function (url, data, method) {
    if (url && data) {
        //convert the data object into input HTML fields
        var inputs = '';
        var convertToInput = function (key, keyStr, obj) {
            if (typeof obj === 'undefined') {
                return;
            } else if (typeof obj === "object") {
                for (var innerKey in obj) {
                    if (obj.hasOwnProperty(innerKey)) {
                        var innerKeyStr = '';
                        if (keyStr === '') {
                            innerKeyStr = innerKey.toString();
                        } else {
                            innerKeyStr = keyStr + "[" + innerKey.toString() + "]";
                        }
                        convertToInput(innerKey, innerKeyStr, obj[innerKey]);
                    }
                }
                return;
            } else if ($.isArray(obj)) {
                obj.forEach(function (item) {
                    convertToInput(key, keyStr + "[]", item);
                });
                return;
            }

            inputs += "<input type='hidden' name='" + keyStr + "' value='" + obj + "' />";
        };
        convertToInput(null, '', data);

        //send request
        jQuery('<form action="' + url + '" method="' + (method || 'post') + '">' + inputs + '</form>').appendTo('body').submit().remove();
    };
};
$.download('/api/search?format=csv', searchData, 'POST');

It probably doesn't make much of a difference, but to provide some context, I've got a javascript and knockout UI calling into WebAPI, MVC4, and nHibernate. The 'format=csv' part of the query string triggers a MediaTypeFormatter to convert the returned models into a CSV file type. If I leave that off, then I get the models back from the API and can populate a Slick grid for display.


I had the same problem but on top use an RESTFUL webservice for this and have an complex data object which i must post.

My solution: like the jQuery Plugin i build a temp formular and submit it. But i send the data object as an parameter with json content (i use here AngularJS but it should work with jQuery.param() too.)

Javascript:

$('<form target="_blank" action="' + appConstants.restbaseurl + '/print/pdf" method="POST">' + 
    "<input name='data' value='" + angular.toJson($scope.versicherung) + "' />" +
    '</form>').appendTo('body').submit().remove();

on the server side we use a CXF REST Service with an JACKSON Provider:

Spring Config:

<jaxrs:server id="masterdataService" address="/">
    <jaxrs:serviceBeans>
        <ref bean="printRestServiceBean" />
    </jaxrs:serviceBeans>
    <jaxrs:providers>
        <bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider" />
        <bean class="de.controller.ExceptionHandler" />
    </jaxrs:providers>
</jaxrs:server>

in the controller i extracted the param and converted it back to an Java Pojo:

package de.controller;

import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;


@Path(Constants.PRINT_PATH)
@Consumes({ MediaType.APPLICATION_JSON, "application/x-www-form-urlencoded"})
@Produces("application/pdf; charset=UTF-8")
public class PrintRestController {

    @Autowired
    private PrintService printService;

    @POST
    @Produces("application/pdf")
    @Path("/pdf")
    public Response getPDF(@FormParam("data") String data) {
        return printService.getPDF(json2Versicherung(data));
    }

    private Versicherung json2Versicherung(String data) {
        Versicherung lVersicherung = null;
        try {
            ObjectMapper mapper = new ObjectMapper();
            lVersicherung = mapper.readValue(data, Versicherung.class);
        } catch(Exception e) {
            LOGGER.error("PrintRestController.json2Versicherung() error", e);
        }
        return lVersicherung;
    }
}

in the PrintService i build the pdf binary and the response:

@Override
public Response getPDF(Versicherung pVersicherung) {
    byte[] result = ... //build the pdf from what ever


    ResponseBuilder response = Response.ok((Object) result);
    response.header("Content-Disposition", "inline; filename=mypdf.pdf");
    return response.build();
}

This solution works for all browsers (even for IE9 which can't handle data url's) and on tablets and smartphone and it have no problems with popupblockers


The jQuery Plugin for Requesting Ajax-like File Downloads is - essentially - creating a form, adding the post data as hidden field(s), adding it to the body of the page, submitting it and removing it.

In my case I did not have a form, only a chunk of data to be posted as it was. That made for the following solution. On the server side I can get the data by simply reading the "data" parameter from the request and URI-decoding it.

function postAndDownload(url, data) {

    encodedData = encodeURIComponent(data);

    $("<form>")
        .attr("action", url)
        .attr("method", "post")
        .append(
            $("input")
                .attr("type", "hidden")
                .attr("name", "data")
                .attr("value", encodedData)
        )
        .appendTo("body")
        .submit()
        .remove();
};


I fail to understand why you want an ajax request to a file download url! But if it's more like client itself generates some content for download - use a data uri. Works perfectly for Chrome and Firefox 20+. Safari and IE NOT! If Flash is allowed, you could use downloadifier.

Ah after reading your code, I see you want to send a bunch of parameters. Well unless the query string gets too long (IE8- has a limit of 2083) why not simply use an anchor with proper url?

    $('a.export-csv').click( function (evt){
      linkEl.attr('href','/export?' + encodeURIComponent(formQueryString()));
      return true;
    });

The above allows you to change the URL before the default event (the click) happens.


I think the best will be to create a temp pdf file in the downloads folder and then load the file using pop-up having an iframe.. chrome will load it instantly but I suppose for other variants Acrobat reader must be installed to view the pdf but again you can use FlashPaper too :)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜