繁体   English   中英

使用 Ajax 下载并打开 PDF 文件

[英]Download and open PDF file using Ajax

我有一个生成 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;
   }
}

我通过 Ajax 调用此action 我不知道如何将这个 stream 传递给浏览器。 我尝试了几件事,但没有任何效果。

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

以上给出了错误:

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

这是我如何工作的

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

为此,您不一定需要 Ajax。 如果您在服务器端代码中将content-dispositionattachment ,只需一个<a>链接就足够了。 这样父页面将保持打开状态,如果这是您的主要关注点(否则您为什么会不必要地为此选择 Ajax?)。 此外,没有办法很好地异步处理这个问题。 PDF 不是字符数据。 是二进制数据。 你不能做像$(element).load()这样的事情。 您想为此使用全新的请求。 <a href="pdfservlet/filename.pdf">pdf</a>非常适合。

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

我真的认为过去的任何答案都没有发现原始海报的问题。 当发布者尝试 POST 数据并获取下载作为响应时,它们都假定一个 GET 请求。

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

在其“核心”中,它创建了一个“临时”HTML 表单,其中包含作为输入字段的给定数据。 此表单将附加到文档并发布到所需的 URL。 紧接着,表单再次被删除:

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

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

这就是我解决这个问题的方法。
Jonathan Amend 在这篇文章中的回答对我帮助很大。
下面的例子是简化的。

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

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 库中。 它是可重复使用的。 希望有帮助。

对我有用的是以下代码,因为服务器函数正在检索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();
                }
});

你可以使用这个插件来创建一个表单,提交它,然后从页面中删除它。

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

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

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

以下代码对我有用

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

要修复 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);
  }
});

希望这可以为您节省几个小时,并使您免于头痛。 我花了一段时间才弄明白,但是执行常规的 $.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);

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

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

});

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

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

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

你必须用 Ajax 来做吗? 难道不能在 iframe 中加载它吗?

如果您必须像我们一样使用文件流(因此没有物理保存的 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>

Javascript

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

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

我找到了一个解决方案,为我解决了这个问题(使用 jquery ajax 时为空白 pdf)。 我在这里找到了这个神奇的解决方案: https : //www.py4u.net/discuss/904599 (答案 2),它涉及将xhrFields 添加到您的 ajax 调用中:

xhrFields: {
   responseType: 'blob'
}

我的工作示例:

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

    })

我希望这将为你们中的许多人解决这个令人讨厌的问题。

所有文件类型 100% OK

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

最好的用法是使用提供的链接做一个锚点或表单,但你需要进行验证或在其他情况下使用 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>

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

然后在 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