使用Ajax下载并打开PDF文件


98

我有一个生成PDF的动作类。该contentType适当地设定。

public class MyAction extends ActionSupport 
{
   public String execute() {
    ...
    ...
    File report = signedPdfExporter.generateReport(xyzData, props);

    inputStream = new FileInputStream(report);
    contentDisposition = "attachment=\"" + report.getName() + "\"";
    contentType = "application/pdf";
    return SUCCESS;
   }
}

action 通过Ajax调用来称呼它。我不知道将流传输到浏览器的方法。我尝试了几件事,但没有任何效果。

$.ajax({
    type: "POST",
    url: url,
    data: wireIdList,
    cache: false,
    success: function(response)
    {
        alert('got response');
        window.open(response);
    },
    error: function (XMLHttpRequest, textStatus, errorThrown) 
    {
        alert('Error occurred while opening fax template' 
              + getAjaxErrorString(textStatus, errorThrown));
    }
});

上面给出了错误:

您的浏览器发送了该服务器无法理解的请求。

Answers:


37

您不一定为此需要Ajax。只是一个<a>环节是不够的,如果你设置content-dispositionattachment服务器端代码。这样,如果您最关心的是父页面将保持打开状态(为什么您为此不必要地选择了Ajax?)。此外,没有办法同时处理这个问题。PDF不是字符数据。它是二进制数据。你不能做类似的事情$(element).load()。您想要对此使用全新的请求。为此<a href="pdfservlet/filename.pdf">pdf</a>是完全合适的。

为了在服务器端代码方面为您提供更多帮助,您需要更多地介绍所使用的语言并发布代码尝试的摘录。


7
再次:你并不需要的Ajax这一点。这只是自找麻烦。PDF是二进制数据,而不是HTML或JSON之类的字符数据。
BalusC 2010年

3
var url = contextPath +“ /xyz/blahBlah.action”; url + = url +“?” +参数;尝试{var child = window.open(url); child.focus(); }(e){}
纳恩(Nayn

5
在某些浏览器中,window.open将保持打开状态并为空白,这对于最终用户而言可能很烦人。因此,也不要为此使用window.open。如果将content-disposition设置为attachment,则只会出现一个Save as对话框。父页面将保持不变。仅仅<a href="pdfservlet/filename.pdf">pdf</a>或一个<form action="pdfservlet/filename.pdf"><input type="submit"></form>绰绰有余。
BalusC 2010年

5
网址长度有限。作者正在询问POST。
爱德华·奥拉米桑

3
同意@EdwardOlamisan,这不是正确的答案,因为作者正在尝试进行POST数据记录。
adamj

122

这是我如何工作的

$.ajax({
  url: '<URL_TO_FILE>',
  success: function(data) {
    var blob=new Blob([data]);
    var link=document.createElement('a');
    link.href=window.URL.createObjectURL(blob);
    link.download="<FILENAME_TO_SAVE_WITH_EXTENSION>";
    link.click();
  }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

使用download.js更新了答案

$.ajax({
  url: '<URL_TO_FILE>',
  success: download.bind(true, "<FILENAME_TO_SAVE_WITH_EXTENSION>", "<FILE_MIME_TYPE>")
});


29
可以在chrome上使用吗?我只能看到空白的pdf。
塔伦·古普塔

1
是的,它确实适用于所有现代浏览器。如果看到空白的pdf,请尝试在新标签页中运行ajax网址。如果在那里也出现黑屏,则pdf本身可能有问题。如果您确实在此处看到pdf文件而不是在下载的文件中,请在我的电子邮件中告诉我。:)
Mayur Padshala 2015年

5
这个(锚元素)实际上在IE 11,Edge和Firefox上对我不起作用。将成功更改为仅使用“ window.open(URL.createObjectURL(blob))”确实有效。
JimiSweden '16

3
pdf文件已下载,但没有内容。我在服务器端保存了byte []并且pdf内容可用。请建议。
Awanish Kumar

4
空白pdf文件已下载。
Farukh

31

我真的不认为过去的任何答案都能发现原始海报的问题。当发布者试图发布数据并获得下载以作为响应时,他们都假定有GET请求。

在寻找更好的答案的过程中,我们找到了这个jQuery插件,用于请求类似Ajax的文件下载

它在其“心脏”中创建一个“临时” HTML表单,其中包含给定的数据作为输入字段。该表格将附加到文档中并发布到所需的URL。之后立即再次删除该表单:

jQuery('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
    .appendTo('body').submit().remove()

与我提到的jQuery插件相比,更新 Mayur的答案看起来非常有前途并且非常简单。


9

这就是我解决这个问题的方式。
乔纳森·阿曼德(Jonathan Amend)在这篇文章上的回答对我很有帮助。
下面的示例已简化。

有关更多详细信息,上述源代码能够使用JQuery Ajax请求(GET,POST,PUT等)下载文件。它还有助于将参数上传为JSON并将内容类型更改为application / json(我的默认设置)

HTML源:

<form method="POST">
    <input type="text" name="startDate"/>
    <input type="text" name="endDate"/>
    <input type="text" name="startDate"/>
    <select name="reportTimeDetail">
        <option value="1">1</option>
    </select>
    <button type="submit"> Submit</button>
</form>  

具有两个输入文本,一个选择和一个按钮元素的简单形式。

JavaScript的网页来源:

<script type="text/javascript" src="JQuery 1.11.0 link"></script>
<script type="text/javascript">
    // File Download on form submition.
    $(document).on("ready", function(){
        $("form button").on("click", function (event) {
            event.stopPropagation(); // Do not propagate the event.

            // Create an object that will manage to download the file.
            new AjaxDownloadFile({
                url: "url that returns a file",
                data: JSON.stringify($("form").serializeObject())
            });

            return false; // Do not submit the form.
        });
    });
</script>  

单击按钮的简单事件。它创建一个AjaxDownloadFile对象。下面是AjaxDownloadFile类的源代码。

AjaxDownloadFile类来源:

var AjaxDownloadFile = function (configurationSettings) {
    // Standard settings.
    this.settings = {
        // JQuery AJAX default attributes.
        url: "",
        type: "POST",
        headers: {
            "Content-Type": "application/json; charset=UTF-8"
        },
        data: {},
        // Custom events.
        onSuccessStart: function (response, status, xhr, self) {
        },
        onSuccessFinish: function (response, status, xhr, self, filename) {
        },
        onErrorOccured: function (response, status, xhr, self) {
        }
    };
    this.download = function () {
        var self = this;
        $.ajax({
            type: this.settings.type,
            url: this.settings.url,
            headers: this.settings.headers,
            data: this.settings.data,
            success: function (response, status, xhr) {
                // Start custom event.
                self.settings.onSuccessStart(response, status, xhr, self);

                // Check if a filename is existing on the response headers.
                var filename = "";
                var disposition = xhr.getResponseHeader("Content-Disposition");
                if (disposition && disposition.indexOf("attachment") !== -1) {
                    var filenameRegex = /filename[^;=\n]*=(([""]).*?\2|[^;\n]*)/;
                    var matches = filenameRegex.exec(disposition);
                    if (matches != null && matches[1])
                        filename = matches[1].replace(/[""]/g, "");
                }

                var type = xhr.getResponseHeader("Content-Type");
                var blob = new Blob([response], {type: type});

                if (typeof window.navigator.msSaveBlob !== "undefined") {
                    // IE workaround for "HTML7007: One or more blob URLs were revoked by closing the blob for which they were created. These URLs will no longer resolve as the data backing the URL has been freed.
                    window.navigator.msSaveBlob(blob, filename);
                } else {
                    var URL = window.URL || window.webkitURL;
                    var downloadUrl = URL.createObjectURL(blob);

                    if (filename) {
                        // Use HTML5 a[download] attribute to specify filename.
                        var a = document.createElement("a");
                        // Safari doesn"t support this yet.
                        if (typeof a.download === "undefined") {
                            window.location = downloadUrl;
                        } else {
                            a.href = downloadUrl;
                            a.download = filename;
                            document.body.appendChild(a);
                            a.click();
                        }
                    } else {
                        window.location = downloadUrl;
                    }

                    setTimeout(function () {
                        URL.revokeObjectURL(downloadUrl);
                    }, 100); // Cleanup
                }

                // Final custom event.
                self.settings.onSuccessFinish(response, status, xhr, self, filename);
            },
            error: function (response, status, xhr) {
                // Custom event to handle the error.
                self.settings.onErrorOccured(response, status, xhr, self);
            }
        });
    };
    // Constructor.
    {
        // Merge settings.
        $.extend(this.settings, configurationSettings);
        // Make the request.
        this.download();
    }
};

我创建了该类以添加到我的JS库中。它是可重用的。希望能有所帮助。


2
BlobIE10 +支持该对象。
暗恋

我必须将responseTypexhr 设置为arraybufferblob使其正常工作。(否则,效果很好。)
tjklemz15年

我有完全相同的问题。所有回答“仅使其成为链接”的人都没有帮助OP。如果您的内容是动态的,而您要链接的内容是动态的,则您必须对其全部进行jquery ...对我来说,答案是在页面上放置一个表单,其中包含所有隐藏的输入(未显示给用户),并且然后填写并使用jquery提交。效果很好。
斯科特(Scott)

这是一个很好的答案,但是由于某种原因,我一直在弄坏空白的PDF。无法解决。当我通过API返回相同的字节集时-很好,所以与MVC响应有关。我使用FileResult响应类型:File(bytes,System.Net.Mime.MediaTypeNames.Application.Octet,fileName);
朱里斯·卡斯坦诺夫

说明:如果通过地址栏打开URL-文件已正确打开。如果我使用AJAX + Blob来获取文件-文件格式错误。
朱里斯·卡斯坦诺夫斯

7

对我有用的是以下代码,因为正在检索服务器功能 File(memoryStream.GetBuffer(), "application/pdf", "fileName.pdf");:

$http.get( fullUrl, { responseType: 'arraybuffer' })
            .success(function (response) {
                var blob = new Blob([response], { type: 'application/pdf' });

                if (window.navigator && window.navigator.msSaveOrOpenBlob) {
                    window.navigator.msSaveOrOpenBlob(blob); // for IE
                }
                else {
                    var fileURL = URL.createObjectURL(blob);
                    var newWin = window.open(fileURL);
                    newWin.focus();
                    newWin.reload();
                }
});

这完全适用于我在此评论的时间,最新的Chrome浏览器
Loredra大号

6

您可以使用此插件来创建表单,然后提交表单,然后将其从页面中删除。

jQuery.download = function(url, data, method) {
    //url and data options required
    if (url && data) {
        //data can be string of parameters or array/object
        data = typeof data == 'string' ? data : jQuery.param(data);
        //split params into form inputs
        var inputs = '';
        jQuery.each(data.split('&'), function() {
            var pair = this.split('=');
            inputs += '<input type="hidden" name="' + pair[0] +
                '" value="' + pair[1] + '" />';
        });
        //send request
        jQuery('<form action="' + url +
                '" method="' + (method || 'post') + '">' + inputs + '</form>')
            .appendTo('body').submit().remove();
    };
};


$.download(
    '/export.php',
    'filename=mySpreadsheet&format=xls&content=' + spreadsheetData
);

这对我有用。在这里找到这个插件


该插件仅创建一个表单,然后提交它,然后将其从页面中删除。(如果有人想知道)
暗恋

4

以下代码对我有用

//Parameter to be passed
var data = 'reportid=R3823&isSQL=1&filter=[]';
var xhr = new XMLHttpRequest();
xhr.open("POST", "Reporting.jsp"); //url.It can pdf file path
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.responseType = "blob";
xhr.onload = function () {
    if (this.status === 200) {
        var blob = new Blob([xhr.response]);
        const url = window.URL.createObjectURL(blob);
        var a = document.createElement('a');
        a.href = url;
        a.download = 'myFile.pdf';
        a.click();
        setTimeout(function () {
            // For Firefox it is necessary to delay revoking the ObjectURL
            window.URL.revokeObjectURL(data)
                , 100
        })
    }
};
xhr.send(data);

4

要解决发布请求中获取PDF之类的流数据的空白PDF问题,我们需要在请求中添加响应类型为“ arraybuffer”或“ blob”

$.ajax({
  url: '<URL>',
  type: "POST",
  dataType: 'arraybuffer',
  success: function(data) {
    let blob = new Blob([data], {type: 'arraybuffer'});
    let link = document.createElement('a');
    let objectURL = window.URL.createObjectURL(blob);
    link.href = objectURL;
    link.target = '_self';
    link.download = "fileName.pdf";
    (document.body || document.documentElement).appendChild(link);
    link.click();
    setTimeout(()=>{
        window.URL.revokeObjectURL(objectURL);
        link.remove();
    }, 100);
  }
});

3

关于Mayur Padshala给出的答案,这是通过ajax下载pdf文件的正确逻辑,但是正如其他人在评论中报告的那样,该解决方案的确下载了空白的pdf。

对此问题的公认答案解释了其原因:jQuery在使用AJAX请求加载二进制数据时存在一些问题,因为它尚未实现某些HTML5 XHR v2功能,请参阅此增强功能请求和此讨论

因此,使用HTMLHTTPRequest代码应如下所示:

var req = new XMLHttpRequest();
req.open("POST", "URL", true);
req.responseType = "blob";
req.onload = function (event) {
    var blob = req.response;
    var link=document.createElement('a');
    link.href=window.URL.createObjectURL(blob);
    link.download="name_for_the_file_to_save_with_extention";
    link.click();
}

2

创建一个隐藏的iframe,然后在上面的ajax代码中:

网址: document.getElementById('myiframeid').src = your_server_side_url

并删除 window.open(response);


这个解决方案像一个魅力。我正在调用服务器端脚本,该脚本对服务进行curl调用,该服务通过curl来获取文件。这非常有用,因为我可以删除加载的gif并禁用请求链接。
eggmatters 2015年

1
此解决方案适用于GET请求,而不适用于原始帖子中的POST请求。
chiccodoro

2

此代码段适用于将面临相同问题的js角度用户,请注意,响应文件是使用编程的click事件下载的。在这种情况下,标头是由包含文件名和内容/类型的服务器发送的。

$http({
    method: 'POST', 
    url: 'DownloadAttachment_URL',
    data: { 'fileRef': 'filename.pdf' }, //I'm sending filename as a param
    headers: { 'Authorization': $localStorage.jwt === undefined ? jwt : $localStorage.jwt },
    responseType: 'arraybuffer',
}).success(function (data, status, headers, config) {
    headers = headers();
    var filename = headers['x-filename'];
    var contentType = headers['content-type'];
    var linkElement = document.createElement('a');
    try {
        var blob = new Blob([data], { type: contentType });
        var url = window.URL.createObjectURL(blob);

        linkElement.setAttribute('href', url);
        linkElement.setAttribute("download", filename);

        var clickEvent = new MouseEvent("click", {
            "view": window,
            "bubbles": true,
            "cancelable": false
        });
        linkElement.dispatchEvent(clickEvent);
    } catch (ex) {
        console.log(ex);
    }
}).error(function (data, status, headers, config) {
}).finally(function () {

});

请为您的答案写一些解释。
Gufran Hasan '18


1

希望这可以节省您几个小时,并使您免于头痛。我花了一些时间才弄清楚这一点,但是执行常规的$ .ajax()请求破坏了我的PDF文件,而通过地址栏请求它却完美地工作了。解决方案是这样的:

包括download.js:http//danml.com/download.html

然后使用XMLHttpRequest代替$ .ajax()请求。

    var ajax = new XMLHttpRequest(); 

    ajax.open("GET", '/Admin/GetPdf' + id, true); 
    ajax.onreadystatechange = function(data) { 
        if (this.readyState == 4)
        {
            if (this.status == 200)
            {
                download(this.response, "report.pdf", "application/pdf");

            }
            else if (this.responseText != "")
            {
                alert(this.responseText);
            }
        }
        else if (this.readyState == 2)
        {
            if (this.status == 200)
            {
                this.responseType = "blob";
            }
            else
            {
                this.responseType = "text";
            }
        }
    };

    ajax.send(null);

0

var xhr;
var beforeSend = function(){
    $('#pleasewaitDL').modal('show');
}
$(function () {
    $('#print_brochure_link').click(function(){
        beforeSend();
        xhr = new XMLHttpRequest();
        xhr.open("GET",$('#preparedPrintModalForm').attr('action'), true); 
        xhr.responseType = "blob";
        xhr.onload = function (e) {
            if (this.status === 200) {
                var file = window.URL.createObjectURL(this.response);
                var a = document.createElement("a");
                a.href = file;
                a.download = this.response.name || "Property Brochure";
                console.log(file);
                document.body.appendChild(a);
                a.click();
                
                window.onfocus = function () {                     
                  document.body.removeChild(a)
                }
                $('#pleasewaitDL').modal('hide');
            };
        };
        xhr.send($('#preparedPrintModalForm').serialize());
    });
    $('#pleasewaitDLCancel').click(function() {
        xhr.abort();
    });
});


0

如果您必须像我们一样使用文件流(因此不需要物理保存的PDF),并且想要下载PDF而无需重新加载页面,则以下功能适用于我们:

的HTML

<div id="download-helper-hidden-container" style="display:none">
     <form id="download-helper-form" target="pdf-download-output" method="post">
            <input type="hidden" name="downloadHelperTransferData" id="downloadHelperTransferData" />
     </form>
     <iframe id="pdf-helper-output" name="pdf-download-output"></iframe>
</div>

Java脚本

var form = document.getElementById('download-helper-form');
$("#downloadHelperTransferData").val(transferData);
form.action = "ServerSideFunctionWhichWritesPdfBytesToResponse";
form.submit();

由于target =“ pdf-download-output”,响应被写入iframe中,因此不执行页面重新加载,但是pdf-response-stream作为下载在浏览器中输出。


抱歉,但是如何获取transferData的值?
凯特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.