简体   繁体   English

使用 Ajax 下载并打开 PDF 文件

[英]Download and open PDF file using Ajax

I have an action class that generates a PDF. The contentType is set appropriately.我有一个生成 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;
   }
}

I call this action through an Ajax call.我通过 Ajax 调用此action I don't know the way to deliver this stream to browser.我不知道如何将这个 stream 传递给浏览器。 I tried a few things but nothing worked.我尝试了几件事,但没有任何效果。

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

The above gives the error:以上给出了错误:

Your browser sent a request that this server could not understand.您的浏览器发送了该服务器无法理解的请求。

Here is how I got this working这是我如何工作的

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

Updated answer using download.js使用download.js更新答案

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

You don't necessarily need Ajax for this.为此,您不一定需要 Ajax。 Just an <a> link is enough if you set the content-disposition to attachment in the server side code.如果您在服务器端代码中将content-dispositionattachment ,只需一个<a>链接就足够了。 This way the parent page will just stay open, if that was your major concern (why would you unnecessarily have chosen Ajax for this otherwise?).这样父页面将保持打开状态,如果这是您的主要关注点(否则您为什么会不必要地为此选择 Ajax?)。 Besides, there is no way to handle this nicely acynchronously.此外,没有办法很好地异步处理这个问题。 PDF is not character data. PDF 不是字符数据。 It's binary data.是二进制数据。 You can't do stuff like $(element).load() .你不能做像$(element).load()这样的事情。 You want to use completely new request for this.您想为此使用全新的请求。 For that <a href="pdfservlet/filename.pdf">pdf</a> is perfectly suitable. <a href="pdfservlet/filename.pdf">pdf</a>非常适合。

To assist you more with the server side code, you'll need to tell more about the language used and post an excerpt of the code attempts.为了在服务器端代码方面为您提供更多帮助,您需要详细说明所使用的语言并发布代码尝试的摘录。

I don't really think that any of the past answers spotted out the problem of the original poster.我真的认为过去的任何答案都没有发现原始海报的问题。 They all presume a GET request while the poster was trying to POST data and get a download in response.当发布者尝试 POST 数据并获取下载作为响应时,它们都假定一个 GET 请求。

In the course of searching for any better answer we found this jQuery Plugin for Requesting Ajax-like File Downloads .在寻找任何更好的答案的过程中,我们找到了这个用于请求类似 Ajax 的文件下载的 jQuery 插件

In its "heart" it creates a "temporary" HTML form containing the given data as input fields.在其“核心”中,它创建了一个“临时”HTML 表单,其中包含作为输入字段的给定数据。 This form is appended to the document and posted to the desired URL.此表单将附加到文档并发布到所需的 URL。 Right after that the form is removed again:紧接着,表单再次被删除:

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

Update Mayur's answer looks pretty promising and very simple in comparison to the jQuery plug-in I referred to.与我提到的 jQuery 插件相比,更新Mayur 的答案看起来非常有希望并且非常简单。

This is how i solve this issue.这就是我解决这个问题的方法。
The answer of Jonathan Amend on this post helped me a lot. Jonathan Amend 在这篇文章中的回答对我帮助很大。
The example below is simplified.下面的例子是简化的。

For more details, the above source code is able to download a file using a JQuery Ajax request (GET, POST, PUT etc) .有关更多详细信息,上述源代码能够使用 JQuery Ajax 请求(GET、POST、PUT 等)下载文件 It, also, helps to upload parameters as JSON and to change the content type to application/json (my default) .它还有助于将参数上传为JSON 并将内容类型更改为 application/json (my default)

The html source: 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>  

A simple form with two input text, one select and a button element.一个简单的表单,有两个输入文本,一个选择和一个按钮元素。

The javascript page source: 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>  

A simple event on button click.一个简单的按钮点击事件。 It creates an AjaxDownloadFile object.它创建了一个 AjaxDownloadFile 对象。 The AjaxDownloadFile class source is below. AjaxDownloadFile 类源如下。

The AjaxDownloadFile class source: 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();
    }
};

I created this class to added to my JS library.我创建了这个类来添加到我的 JS 库中。 It is reusable.它是可重复使用的。 Hope that helps.希望有帮助。

What worked for me is the following code, as the server function is retrieving File(memoryStream.GetBuffer(), "application/pdf", "fileName.pdf");:对我有用的是以下代码,因为服务器函数正在检索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();
                }
});

You could use this plugin which creates a form, and submits it, then removes it from the page.你可以使用这个插件来创建一个表单,提交它,然后从页面中删除它。

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

This worked for me.这对我有用。 Found this plugin here 在这里找到了这个插件

Concerning the answer given by Mayur Padshala this is the correct logic to download a pdf file via ajax but as others report in the comments this solution is indeed downloads a blank pdf.关于Mayur Padshala给出的答案,这是通过 ajax 下载 pdf 文件的正确逻辑,但正如其他人在评论中报告的那样,该解决方案确实下载了一个空白的 pdf。

The reason for this is explained in the accepted answer of this question : jQuery has some issues loading binary data using AJAX requests, as it does not yet implement some HTML5 XHR v2 capabilities, see this enhancement request and this discussion .其原因在此问题的已接受答案中进行了解释:jQuery 在使用 AJAX 请求加载二进制数据时存在一些问题,因为它尚未实现某些 HTML5 XHR v2 功能,请参阅此增强请求和此讨论

So using HTMLHTTPRequest the code should look like this:所以使用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();
}

The following code worked for me以下代码对我有用

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

To fix the blank PDF issue in post request to get stream data like PDF, we need to add response type as 'arraybuffer' or 'blob' in request要修复 post 请求中的空白 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);
  }
});

Hope this will save you a few hours and spare you from a headache.希望这可以为您节省几个小时,并使您免于头痛。 It took me a while to figure this out, but doing regular $.ajax() request ruined my PDF file, while requesting it through address bar worked perfectly.我花了一段时间才弄明白,但是执行常规的 $.ajax() 请求破坏了我的 PDF 文件,而通过地址栏请求它工作得很好。 Solution was this:解决方案是这样的:

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

Then use XMLHttpRequest instead of $.ajax() request.然后使用 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);

This snippet is for angular js users which will face the same problem, Note that the response file is downloaded using a programmed click event.此代码段适用于将面临相同问题的 angular js 用户,请注意,响应文件是使用编程的单击事件下载的。 In this case , the headers were sent by server containing filename and content/type.在这种情况下,标头由包含文件名和内容/类型的服务器发送。

$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 () {

});

create a hidden iframe, then in your ajax code above:创建一个隐藏的 iframe,然后在上面的 ajax 代码中:

url: document.getElementById('myiframeid').src = your_server_side_url , url: document.getElementById('myiframeid').src = your_server_side_url ,

and remove the window.open(response);并删除window.open(response);

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

Do you have to do it with Ajax?你必须用 Ajax 来做吗? Coouldn't it be a possibility to load it in an iframe?难道不能在 iframe 中加载它吗?

If you have to work with file-stream (so no physically saved PDF) like we do and you want to download the PDF without page-reload, the following function works for us:如果您必须像我们一样使用文件流(因此没有物理保存的 PDF),并且您想在不重新加载页面的情况下下载 PDF,以下功能对我们有用:

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

Javascript Javascript

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

Due to the target="pdf-download-output" , the response is written into the iframe and therefore no page reload is executed, but the pdf-response-stream is output in the browser as a download.由于target="pdf-download-output" ,响应被写入 iframe,因此不会执行页面重新加载,但 pdf-response-stream 在浏览器中作为下载输出。

I have found a solution that solved this problem for me (blank pdf when using jquery ajax).我找到了一个解决方案,为我解决了这个问题(使用 jquery ajax 时为空白 pdf)。 I've found this magical solution here: https://www.py4u.net/discuss/904599 (Answer 2) and it involves adding xhrFields to your ajax call:我在这里找到了这个神奇的解决方案: https : //www.py4u.net/discuss/904599 (答案 2),它涉及将xhrFields 添加到您的 ajax 调用中:

xhrFields: {
   responseType: 'blob'
}

My working example:我的工作示例:

$.ajax({
      url: "myUrl",
      type: 'GET',
      headers: {"token": mySecurityToken},
      xhrFields: {
                responseType: 'blob'
      },
      data: {id: myId}
    }).done(function( data, statusText, xhr ) {
        var filename = "";
        var disposition = xhr.getResponseHeader("Content-Disposition");
        if (disposition && (disposition.indexOf("attachment") !== -1) || disposition.indexOf("filename") !== -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([data], {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
        }

    })

I hope this will solve this nasty issue for many of you.我希望这将为你们中的许多人解决这个令人讨厌的问题。

100% OK for all file types所有文件类型 100% OK

// download the file
var link = document.createElement('a'),
    filename = fname;
link.href = URL.createObjectURL(data);
link.download = filename;
link.click();

The best usage is to do an anchor or a form with the provided link, but it you need to do a validation or in other cases using jquery the best usage is to add a form and submit it using jquery (don't forget to set your request disposition as attachement on server side).最好的用法是使用提供的链接做一个锚点或表单,但你需要进行验证或在其他情况下使用 jquery 最好的用法是添加一个表单并使用 jquery 提交它(不要忘记设置您的请求处理作为服务器端的附件)。

<form id="pdf-form" action="/link_to/download_your.pdf" accept-charset="UTF-8" method="get">
    <input type="hidden" name="data" id="data" value="your data"></form>

and

<a href="javascript:void(0);" id="pdf">Download my Pdf</a>

then in jquery然后在 jquery

$('#pdf').click(function () {
// your data if it json do it like this JSON.stringify(your_data_as_json)
$('#data').val(data);
$('#pdf-form').submit();
})

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM