POST 到服务器,接收 PDF,使用 jQuery 交付给用户

Posted

技术标签:

【中文标题】POST 到服务器,接收 PDF,使用 jQuery 交付给用户【英文标题】:POST to server, receive PDF, deliver to user w/ jQuery 【发布时间】:2011-01-12 07:04:34 【问题描述】:

我有一个链接,用户单击该链接即可获取 PDF。在 jQuery 中,我创建了一个对服务器的 POST ajax 调用以获取 PDF。 PDF 带有正确的内容标题等,通常会导致浏览器打开 Reader 插件,或允许用户保存 PDF。

由于我正在获取带有 ajax 调用的 PDF,因此我不确定如何处理在 OnSuccess 回调中获得的数据。如何将收到的数据提供给浏览器并允许它使用 PDF 响应执行其默认操作?

【问题讨论】:

这将是浏览器通常对内容类型设置为 PDF 的响应所做的任何事情。打开阅读器,提示下载位置等。 【参考方案1】:

看看-jQuery Plugin for Requesting Ajax-like File Downloads

整个plugin 大约只有 30 行代码(包括 cmets)。

该调用与 jquery ajax 调用非常相似。

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

当然,您必须在服务器端设置 content-type 和 Content-Disposition 标头,就像任何此类下载一样。

在java中我会做这样的事情

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

【讨论】:

这是答案,但我没有足够的声誉来投票。我知道我以错误的方式思考问题。【参考方案2】:

你根本不需要 jQuery。只需通过表单正常提交您的 POST,然后在服务器端添加 HTTP 标头

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

浏览器会做它默认的事情。

或者,如果您想更加小心地报告在 PDF 生成期间可能发生的任何错误,您可以这样做。使用 jQuery 将您的参数发布到您的服务器。在服务器上,生成二进制内容并将其缓存在某处几分钟,可通过您放入用户会话中的密钥访问,并向您的页面返回“成功”Ajax 响应(或者如果出现错误,则返回“错误”响应)。如果页面返回成功响应,它可以立即执行以下操作:

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

然后服务器返回缓存的 PDF 内容。请务必包含 Content-Disposition 标头,如上所述。

【讨论】:

你可能不需要,但有时你需要;)【参考方案3】:

提到“用于请求类似 Ajax 的文件下载的 jQuery 插件”的答案让我朝着正确的方向前进,但它并不完全适合我的情况,因为我有一个复杂的对象和对象数组作为我的传递搜索条件/过滤数据。我想我会分享我的代码,以防其他人也遇到这种情况。

$.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');

这可能没有太大区别,但为了提供一些上下文,我有一个 javascript 和淘汰 UI 调用到 WebAPI、MVC4 和 nHibernate。查询字符串的 'format=csv' 部分触发 MediaTypeFormatter 将返回的模型转换为 CSV 文件类型。如果我不这样做,那么我会从 API 中获取模型,并可以填充 Slick 网格以进行显示。

【讨论】:

【参考方案4】:

我遇到了同样的问题,但最重要的是使用RESTFUL webservice 并有一个我必须发布的复杂数据对象。

我的解决方案: 就像 jQuery 插件一样,我构建了一个临时公式并提交它。但是我将数据对象作为带有json内容的参数发送(我在这里使用AngularJS,但它也应该与jQuery.param()一起使用。)

Javascript:

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

在服务器端,我们使用 CXF REST ServiceJACKSON 提供者:

弹簧配置:

<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>

在控制器中,我提取了参数并将其转换回 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;
    

在 PrintService 中,我构建了 pdf 二进制文件和响应:

@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();

此解决方案适用于所有浏览器(甚至适用于无法处理数据 url 的 IE9)以及平板电脑和智能手机,并且弹出阻止程序没有问题

【讨论】:

你无法告诉用户下载已经完成,对吧? 不,除非您使用一些额外的技巧,例如设置cookies【参考方案5】:

用于请求类似 Ajax 的文件下载的 jQuery 插件本质上是创建一个表单,将发布数据添加为隐藏字段,将其添加到页面主体,提交并删除它。

在我的情况下,我没有表格,只有一块数据要按原样发布。这导致了以下解决方案。在服务器端,我可以通过简单地从请求中读取“数据”参数并对其进行 URI 解码来获取数据。

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();
;

【讨论】:

【参考方案6】:

我不明白您为什么要对文件下载 url 发出 ajax 请求!但是,如果它更像是客户端本身生成一些供下载的内容 - 使用数据 uri。适用于 Chrome 和 Firefox 20+。 Safari 和 IE 不是!如果允许 Flash,您可以使用下载器。

啊,看了你的代码,我看到你想发送一堆参数。好吧,除非查询字符串太长(IE8- 限制为 2083),否则为什么不简单地使用具有正确 url 的锚点呢?

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

以上允许您在默认事件(点击)发生之前更改 URL。

【讨论】:

啊,看了你的代码,我看到你要发送一堆参数。好吧,除非查询字符串太长(IE8- 限制为 2083),否则为什么不简单地使用具有正确 url 的锚点? 您的代码看起来不错,我尝试了类似的方法,它似乎可以工作。你能概括一下哪一部分不适合你吗?【参考方案7】:

我认为最好的办法是在下载文件夹中创建一个临时 pdf 文件,然后使用具有 iframe 的弹出窗口加载该文件.. chrome 会立即加载它,但我想对于其他变体 Acrobat 阅读器必须安装到查看 pdf,但您也可以使用 FlashPaper :)

【讨论】:

答案与问题的上下文相去甚远,根本没有提供任何细节

以上是关于POST 到服务器,接收 PDF,使用 jQuery 交付给用户的主要内容,如果未能解决你的问题,请参考以下文章

使用 jQuery ajax post 接收 PHP 参数

ChromeCast 可以接收 POST 消息到服务吗?

php如何接收别的服务器post过来的数据 - 技术问答

当使用 okhttp 发送 post 请求时,服务器接收到的请求正文为空。可能是啥问题?

使用 AVAudioPlayer 从 HTTP POST 请求播放接收到的 NSData

Chrome、pdf 显示、从服务器接收到的重复标头