Tải xuống và mở tệp PDF bằng Ajax


98

Tôi có một lớp hành động tạo tệp PDF. Các contentTypeđược thiết lập một cách thích hợp.

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

Tôi gọi điều này action thông qua một cuộc gọi Ajax. Tôi không biết cách chuyển luồng này tới trình duyệt. Tôi đã thử một vài điều nhưng không có hiệu quả.

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

Ở trên đưa ra lỗi:

Trình duyệt của bạn đã gửi một yêu cầu mà máy chủ này không thể hiểu được.

Câu trả lời:


37

Bạn không nhất thiết phải cần Ajax cho việc này. Chỉ cần một <a>liên kết là đủ nếu bạn đặt content-dispositionđể attachmenttrong mã phía máy chủ. Bằng cách này, trang mẹ sẽ vẫn mở, nếu đó là mối quan tâm chính của bạn (tại sao bạn lại chọn Ajax cho việc này một cách không cần thiết?). Bên cạnh đó, không có cách nào để xử lý điều này một cách độc đáo không đồng bộ. PDF không phải là dữ liệu ký tự. Đó là dữ liệu nhị phân. Bạn không thể làm những thứ như thế $(element).load(). Bạn muốn sử dụng yêu cầu hoàn toàn mới cho việc này. Vì điều đó<a href="pdfservlet/filename.pdf">pdf</a> là hoàn toàn phù hợp.

Để hỗ trợ bạn nhiều hơn với mã phía máy chủ, bạn sẽ cần cho biết thêm về ngôn ngữ được sử dụng và đăng đoạn trích về các lần thử mã.


7
Một lần nữa: bạn không cần Ajax cho việc này. Nó chỉ yêu cầu rắc rối. PDF là dữ liệu nhị phân, không phải dữ liệu ký tự như HTML hoặc JSON.
BalusC

3
var url = contextPath + "/xyz/blahBlah.action"; url + = url + "?" + params; thử {var child = window.open (url); con.focus (); } catch (e) {}
Nayn

5
Trong một số trình duyệt, window.open sẽ luôn mở và trống, điều này có thể gây khó chịu cho người dùng cuối. Vì vậy, cũng KHÔNG sử dụng window.open cho việc này. Nếu content-dispositionđược đặt thành attachment, bạn sẽ chỉ nhận được một đoạn hội Save asthoại. Trang mẹ sẽ không thay đổi. Chỉ cần <a href="pdfservlet/filename.pdf">pdf</a>hoặc a <form action="pdfservlet/filename.pdf"><input type="submit"></form>là quá đủ.
BalusC

5
Có độ dài Url giới hạn. Và tác giả đang hỏi về POST.
Edward Olamisan

3
Đồng ý với @EdwardOlamisan, đây không phải là một câu trả lời chính xác vì tác giả đang cố gắng POSTdữ liệu.
adamj

122

Đây là cách tôi làm việc này

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

Cập nhật câu trả lời bằng cách sử dụng download.js

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


29
Nó có hoạt động trên chrome không? Tôi chỉ có thể nhìn thấy một bản pdf trống.
Tarun Gupta

1
Có, nó hoạt động trên tất cả các trình duyệt hiện đại. Nếu bạn thấy một pdf trống, hãy thử chạy url ajax trong một tab mới. Nếu bạn cũng nhận được một màn hình trống ở đó, có thể là sự cố với bản thân pdf. Nếu bạn thấy tệp pdf ở đó mà không có trong tệp đã tải xuống, hãy cho tôi biết trên email của tôi. :)
Mayur Padshala

5
Điều này (phần tử neo) thực sự không hoạt động đối với tôi trên IE 11, Edge và Firefox. thay đổi thành công thành chỉ sử dụng "window.open (URL.createObjectURL (blob))" đã hoạt động.
JimiSweden 19/07/2016

3
tệp pdf được tải xuống nhưng không có nội dung nào có sẵn. tôi có byte lưu [] ở phía máy chủ và nội dung pdf có sẵn. làm ơn đề nghị.
Awanish Kumar

4
tệp pdf trống được tải xuống.
Farukh

31

Tôi không thực sự nghĩ rằng bất kỳ câu trả lời nào trong quá khứ đã phát hiện ra vấn đề của tấm áp phích gốc. Tất cả họ đều giả định một yêu cầu GET trong khi người đăng đang cố gắng ĐĂNG dữ liệu và tải xuống để phản hồi.

Trong quá trình tìm kiếm bất kỳ câu trả lời nào tốt hơn, chúng tôi đã tìm thấy Plugin jQuery này để Yêu cầu Tải xuống tệp giống như Ajax .

Trong "trái tim" của nó, nó tạo ra một dạng HTML "tạm thời" chứa dữ liệu đã cho dưới dạng các trường đầu vào. Biểu mẫu này được thêm vào tài liệu và được đăng lên URL mong muốn. Ngay sau đó biểu mẫu lại bị xóa:

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

Câu trả lời của Cập nhật Mayur trông khá hứa hẹn và rất đơn giản so với plugin jQuery mà tôi đã đề cập.


9

Đây là cách tôi giải quyết vấn đề này.
Câu trả lời của Jonathan Amend trên bài đăng này đã giúp tôi rất nhiều.
Ví dụ dưới đây được đơn giản hóa.

Để biết thêm chi tiết, mã nguồn ở trên có thể tải xuống tệp bằng yêu cầu JQuery Ajax (GET, POST, PUT, v.v.) . Nó cũng giúp tải lên các tham số dưới dạng JSON và thay đổi loại nội dung thành application / json (mặc định của tôi) .

Nguồn 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>  

Một biểu mẫu đơn giản với hai văn bản đầu vào, một lựa chọn và một phần tử nút.

Nguồn trang 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>  

Một sự kiện đơn giản khi nhấp vào nút. Nó tạo một đối tượng AjaxDownloadFile. Nguồn lớp AjaxDownloadFile ở bên dưới.

Nguồn lớp 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();
    }
};

Tôi đã tạo lớp này để thêm vào thư viện JS của mình. Nó có thể tái sử dụng. Hy vọng rằng sẽ giúp.


2
Blobđối tượng được hỗ trợ trong IE10 +.
nghiền nát

Tôi đã phải đặt responseTypexhr để arraybufferhoặc blobđể điều này hoạt động. (Nếu không, điều này hoạt động tuyệt vời.)
tjklemz

Tôi đã có cùng một câu hỏi. Tất cả những người trả lời "chỉ làm cho nó thành một liên kết" không giúp OP. Nếu nội dung của bạn là động và liên kết bạn đến là động, bạn phải truy vấn tất cả ... câu trả lời cho tôi là đặt một biểu mẫu trên trang với tất cả các đầu vào ẩn (không được hiển thị cho người dùng) và sau đó điền vào và gửi nó bằng jquery. Hoạt động tuyệt vời.
Scott

Đây là một câu trả lời tuyệt vời, nhưng vì một số lý do, tôi tiếp tục nhận được tệp PDF trống rỗng. Không thể tìm ra nó. Khi tôi trả về cùng một byte thông qua API - nó ổn, vì vậy đó là điều cần làm với phản hồi MVC. Tôi sử dụng loại phản hồi FileResult: File (byte, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
Jurijs Kastanovs

Làm rõ: nếu mở URL thông qua thanh địa chỉ - tệp được mở chính xác. Nếu tôi sử dụng AJAX + blob để lấy tệp - tệp không đúng định dạng.
Jurijs Kastanovs

7

Điều làm việc cho tôi là đoạn mã sau, vì chức năng máy chủ đang truy xuất 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();
                }
});

Này làm việc cho tôi một cách hoàn hảo tại thời điểm nhận xét này và mới nhất của Chrome
Loredra L

6

Bạn có thể sử dụng plugin này để tạo một biểu mẫu và gửi nó, sau đó xóa nó khỏi trang.

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

Điều này đã làm việc cho tôi. Tìm thấy plugin này ở đây


Plugin này chỉ tạo một biểu mẫu và gửi nó, sau đó xóa nó khỏi trang. (nếu có ai đang thắc mắc)
nghiền nát

4

Đoạn mã sau phù hợp với tôi

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

Để khắc phục sự cố PDF trống trong yêu cầu đăng để nhận dữ liệu luồng như PDF, chúng tôi cần thêm loại phản hồi là 'bộ đệm mảng' hoặc 'blob' trong yêu cầu

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

Liên quan đến câu trả lời do Mayur Padshala đưa ra, đây là logic chính xác để tải xuống tệp pdf qua ajax nhưng như những người khác báo cáo trong các nhận xét, giải pháp này thực sự tải xuống một tệp pdf trống.

Lý do cho điều này được giải thích trong câu trả lời được chấp nhận của câu hỏi này : jQuery có một số vấn đề khi tải dữ liệu nhị phân bằng cách sử dụng các yêu cầu AJAX, vì nó chưa triển khai một số khả năng HTML5 XHR v2, hãy xem yêu cầu nâng cao này và thảo luận này .

Vì vậy, việc sử dụng HTMLHTTPRequestmã sẽ giống như sau:

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

tạo iframe ẩn, sau đó trong mã ajax của bạn ở trên:

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

và loại bỏ window.open(response);


Giải pháp này hoạt động như một sự quyến rũ. Tôi đang gọi một tập lệnh phía máy chủ thực hiện lệnh gọi curl tới một dịch vụ tìm nạp tệp qua curl. Điều này hoạt động tốt vì tôi có thể thả một gif đang tải và vô hiệu hóa liên kết yêu cầu.
Eggmatters

1
Giải pháp này hoạt động cho các yêu cầu GET không phải cho các yêu cầu POST như trong bài đăng gốc.
chiccodoro

2

Đoạn mã này dành cho người dùng js góc cạnh sẽ gặp phải vấn đề tương tự, Lưu ý rằng tệp phản hồi được tải xuống bằng sự kiện nhấp chuột được lập trình. Trong trường hợp này, tiêu đề được gửi bởi máy chủ chứa tên tệp và nội dung / loại.

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

});

Vui lòng viết một số giải thích cho câu trả lời của bạn.
Gufran Hasan,

1

Bạn có phải làm điều đó với Ajax? Có thể tải nó trong iframe không?


1
Tôi đang kiểm tra xem, điều này có thể được thực hiện với Ajax. Nếu nó là không thể về mặt kỹ thuật hoặc một cách tiếp cận kém hơn, tôi sẽ chuyển sang các cách tiếp cận khác.
Nayn

1

Hy vọng điều này sẽ giúp bạn tiết kiệm một vài giờ và giúp bạn khỏi đau đầu. Tôi đã mất một lúc để tìm ra điều này, nhưng việc thực hiện yêu cầu $ .ajax () thông thường đã làm hỏng tệp PDF của tôi, trong khi yêu cầu thông qua thanh địa chỉ hoạt động hoàn hảo. Giải pháp là:

Bao gồm download.js: http://danml.com/download.html

Sau đó, sử dụng XMLHttpRequest thay vì yêu cầu $ .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

Nếu bạn phải làm việc với dòng tệp (vì vậy không có tệp PDF được lưu về mặt vật lý) như chúng tôi và bạn muốn tải xuống tệp PDF mà không cần tải lại trang, chức năng sau sẽ hoạt động với chúng tôi:

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

Do target = "pdf-download-output" , phản hồi được ghi vào iframe và do đó không có quá trình tải lại trang nào được thực hiện, nhưng pdf-response-stream được xuất trong trình duyệt dưới dạng tải xuống.


xin lỗi, nhưng làm thế nào để bạn nhận được giá trị transferData?
Kate
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.