使用Ajax下载并打开PDF文件

时间:2010-01-04 13:42:20

标签: javascript java jquery pdf

我有一个生成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。我不知道将此流传递给浏览器的方法。我尝试了一些但没有任何效果。

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

上面给出了错误:

  

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

15 个答案:

答案 0 :(得分:111)

以下是我的工作方式



$.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();
  }
});
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
&#13;
&#13;
&#13;

使用download.js

更新了答案

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

答案 1 :(得分:34)

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

为了更多地帮助您使用服务器端代码,您需要详细了解所使用的语言并发布代码尝试的摘录。

答案 2 :(得分:30)

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

在搜索任何更好的答案的过程中,我们发现了jQuery Plugin for Requesting Ajax-like File Downloads

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

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

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

答案 3 :(得分:9)

这就是我解决这个问题的方法 Jonathan Amend对this post的回答对我帮助很大 以下示例已简化。

有关更多详细信息,上述源代码能够使用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>  

一个简单的表单,包含两个输入文本,一个select和一个button元素。

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

答案 4 :(得分:5)

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

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

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

答案 5 :(得分:5)

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

答案 6 :(得分:2)

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

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

并删除window.open(response);

答案 7 :(得分:1)

您是否必须使用Ajax?难道不能将它加载到iframe中吗?

答案 8 :(得分:1)

此片段适用于角度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 () {

});

答案 9 :(得分:1)

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

question接受的答案中解释了这个原因:jQuery在使用AJAX请求加载二进制数据时存在一些问题,因为它尚未实现某些HTML5 XHR v2功能,请参阅此增强{{3这个request

因此,使用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();
}

答案 10 :(得分:1)

以下代码对我有用

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

答案 11 :(得分:1)

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

npm run build

答案 12 :(得分: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();
    });
});

答案 13 :(得分: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>

的Javascript

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

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

答案 14 :(得分:0)

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