Unduh dan buka file PDF menggunakan Ajax

98

Saya memiliki kelas tindakan yang menghasilkan PDF. Ini contentTypedisetel dengan tepat.

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

Saya menyebutnya action melalui panggilan Ajax. Saya tidak tahu cara mengirimkan streaming ini ke browser. Saya mencoba beberapa hal tetapi tidak ada yang berhasil.

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

Di atas memberikan kesalahan:

Browser Anda mengirimkan permintaan yang tidak dapat dipahami server ini.

Nayn
sumber

Jawaban:

37

Anda tidak perlu Ajax untuk ini. Hanya <a>link cukup jika Anda mengatur content-dispositionuntuk attachmentdalam kode sisi server. Dengan cara ini halaman induk akan tetap terbuka, jika itu adalah perhatian utama Anda (mengapa Anda tidak perlu memilih Ajax untuk ini?). Selain itu, tidak ada cara untuk menangani ini dengan baik secara sinkron. PDF bukanlah data karakter. Ini data biner. Anda tidak dapat melakukan hal-hal seperti itu $(element).load(). Anda ingin menggunakan permintaan yang benar-benar baru untuk ini. Untuk itu <a href="pdfservlet/filename.pdf">pdf</a>sangat cocok.

Untuk membantu Anda lebih banyak dengan kode sisi server, Anda harus memberi tahu lebih banyak tentang bahasa yang digunakan dan memposting kutipan dari upaya kode.

BalusC
sumber
7
Sekali lagi: Anda tidak membutuhkan Ajax untuk ini. Itu hanya mencari masalah. PDF adalah data biner, bukan data karakter seperti HTML atau JSON.
BalusC
3
var url = contextPath + "/xyz/blahBlah.action"; url + = url + "?" + params; coba {var child = window.open (url); child.focus (); } menangkap (e) {}
Nayn
5
Di beberapa browser, window.open akan tetap terbuka dan kosong, yang mungkin mengganggu pengguna akhir. Jadi, JANGAN juga gunakan window.open untuk ini. Jika content-dispositiondisetel ke attachment, Anda hanya akan mendapatkan Save asdialog. Halaman induk tidak akan berubah. Just <a href="pdfservlet/filename.pdf">pdf</a>or a <form action="pdfservlet/filename.pdf"><input type="submit"></form>lebih dari cukup.
BalusC
5
Panjang Url terbatas. Dan penulis bertanya tentang POST.
Edward Olamisan
3
Setuju dengan @EdwardOlamisan, ini bukan jawaban yang benar karena penulis mencoba POSTdata.
adamj
123

Inilah cara saya membuat ini berfungsi

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

Jawaban yang diperbarui menggunakan download.js

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

Mayur Padshala
sumber
29
Apakah ini berfungsi di chrome? Saya hanya bisa melihat pdf kosong.
Tarun Gupta
1
Ya, ini berfungsi di semua browser modern. Jika Anda melihat pdf kosong, coba jalankan url ajax di tab baru. Jika Anda mendapatkan layar kosong di sana juga, mungkin ada masalah dengan pdf itu sendiri. Jika Anda melihat file pdf di sana dan tidak ada di file yang diunduh, beri tahu saya di email saya. :)
Mayur Padshala
5
Ini (elemen jangkar) sebenarnya tidak berfungsi untuk saya di IE 11, Edge, dan Firefox. mengubah keberhasilan menjadi hanya menggunakan "window.open (URL.createObjectURL (blob))" berhasil.
JimiSweden
3
File pdf diunduh tetapi tidak ada konten yang tersedia. saya telah menyimpan byte [] di sisi server dan konten pdf tersedia. tlg menyarankan.
Awanish Kumar
4
file pdf kosong diunduh.
Farukh
31

Saya tidak benar-benar berpikir bahwa jawaban masa lalu menemukan masalah dari poster asli. Mereka semua menganggap permintaan GET saat pembuat poster mencoba POST data dan mendapatkan unduhan sebagai tanggapan.

Dalam proses mencari jawaban yang lebih baik, kami menemukan Plugin jQuery ini untuk Meminta Unduhan File seperti Ajax .

Dalam "jantungnya", ini membuat formulir HTML "sementara" yang berisi data yang diberikan sebagai bidang masukan. Formulir ini ditambahkan ke dokumen dan diposting ke URL yang diinginkan. Segera setelah itu formulir tersebut dihapus lagi:

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

Perbarui jawaban Mayur terlihat cukup menjanjikan dan sangat sederhana dibandingkan dengan plug-in jQuery yang saya rujuk.

chiccodoro.dll
sumber
9

Beginilah cara saya mengatasi masalah ini.
Jawaban dari Jonathan Amend pada posting ini sangat membantu saya.
Contoh di bawah ini disederhanakan.

Untuk lebih jelasnya, source code di atas dapat mendownload file menggunakan request JQuery Ajax (GET, POST, PUT dll) . Ini, juga, membantu mengunggah parameter sebagai JSON dan mengubah tipe konten ke application / json (default saya) .

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

Bentuk sederhana dengan dua teks masukan, satu pilih dan satu elemen tombol.

The Halaman javascript Sumber:

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

Acara sederhana dengan mengklik tombol. Ini membuat objek AjaxDownloadFile. Sumber kelas AjaxDownloadFile ada di bawah.

The AjaxDownloadFile kelas sumber:

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

Saya membuat kelas ini untuk ditambahkan ke perpustakaan JS saya. Ini dapat digunakan kembali. Semoga membantu.

George Siggouroglou
sumber
2
Blobobjek didukung di IE10 +.
hancurkan
Saya harus mengatur responseTypexhr ke arraybufferatau blobagar ini berfungsi. (Jika tidak, ini bekerja dengan baik.)
tjklemz
Saya punya pertanyaan yang persis sama. Semua orang yang menanggapi "jadikan saja sebagai tautan" tidak membantu OP. Jika konten Anda dinamis dan link yang akan Anda tuju dinamis, Anda harus menanyakan semuanya ... jawaban bagi saya adalah meletakkan formulir di halaman dengan semua input tersembunyi (tidak ditampilkan kepada pengguna) dan lalu isi dan kirimkan dengan jquery. Bekerja dengan baik.
Scott
Ini adalah jawaban yang bagus, tetapi untuk beberapa alasan, saya terus saja memecahkan PDF kosong. Tidak bisa memahaminya. Ketika saya mengembalikan byte yang sama melalui API - tidak apa-apa, jadi ini ada hubungannya dengan respons MVC. Saya menggunakan jenis respons FileResult: File (byte, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
Jurijs Kastanovs
Klarifikasi: jika membuka URL melalui bilah alamat - file dibuka dengan benar. Jika saya menggunakan AJAX + blob untuk mendapatkan file - file yang rusak.
Jurijs Kastanovs
7

Apa yang berhasil untuk saya adalah kode berikut, karena fungsi server sedang mengambil 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();
                }
});
ParPar
sumber
Ini berfungsi untuk saya dengan sempurna pada saat komentar ini dan Chrome terbaru
Loredra L
6

Anda dapat menggunakan plugin ini untuk membuat formulir, dan mengirimkannya, lalu menghapusnya dari halaman.

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

Ini berhasil untuk saya. Temukan plugin ini di sini

Ijas Ameenudeen
sumber
Plugin ini hanya membuat formulir, dan mengirimkannya, lalu menghapusnya dari halaman. (jika ada yang bertanya-tanya)
hancurkan
4

Kode berikut berhasil untuk saya

//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);
MemZ
sumber
4

Untuk memperbaiki masalah PDF kosong dalam permintaan posting untuk mendapatkan data streaming seperti PDF, kita perlu menambahkan jenis respons sebagai 'arraybuffer' atau 'blob' dalam permintaan

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

Mengenai jawaban yang diberikan oleh Mayur Padshala ini adalah logika yang benar untuk mendownload file pdf melalui ajax tetapi seperti yang lain laporkan di komentar solusi ini memang mendownload pdf kosong.

Alasan untuk hal ini dijelaskan dalam jawaban yang diterima dari pertanyaan ini : jQuery memiliki beberapa masalah saat memuat data biner menggunakan permintaan AJAX, karena belum mengimplementasikan beberapa kemampuan HTML5 XHR v2, lihat permintaan peningkatan ini dan diskusi ini .

Jadi menggunakan HTMLHTTPRequestkode akan terlihat seperti ini:

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

buat iframe tersembunyi, lalu di kode ajax Anda di atas:

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

dan hapus window.open(response);

qalhat
sumber
Solusi ini bekerja dengan sangat baik. Saya memanggil skrip sisi server yang membuat panggilan curl ke layanan yang mengambil file melalui curl. Ini berfungsi dengan baik karena saya dapat menjatuhkan gif yang memuat dan menonaktifkan tautan permintaan.
eggmatters
1
Solusi ini berfungsi untuk permintaan GET bukan untuk permintaan POST seperti pada posting asli.
chiccodoro
2

Cuplikan ini untuk pengguna angular js yang akan menghadapi masalah yang sama, Perhatikan bahwa file respons diunduh menggunakan peristiwa klik terprogram. Dalam hal ini, header dikirim oleh server yang berisi nama file dan konten / jenis.

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

});
Gihan Sandaru
sumber
Mohon tuliskan penjelasan untuk jawaban Anda.
Gufran Hasan
1

Apakah Anda harus melakukannya dengan Ajax? Bukankah itu kemungkinan untuk memuatnya dalam iframe?

Emil Vikström
sumber
1
Saya sedang memeriksa apakah, ini bisa dilakukan dengan Ajax. Jika secara teknis tidak mungkin atau pendekatan yang lebih rendah, saya akan beralih ke pendekatan lain.
Nayn
1

Semoga ini akan menyelamatkan Anda beberapa jam dan menghindarkan Anda dari sakit kepala. Butuh beberapa saat bagi saya untuk memikirkan hal ini, tetapi melakukan permintaan $ .ajax () biasa merusak file PDF saya, sementara memintanya melalui bilah alamat bekerja dengan sempurna. Solusinya adalah ini:

Sertakan download.js: http://danml.com/download.html

Kemudian gunakan XMLHttpRequest sebagai ganti permintaan $ .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);
Jurijs Kastanovs
sumber
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();
    });
});

POGSNET
sumber
0

Jika Anda harus bekerja dengan aliran file (jadi tidak ada PDF yang disimpan secara fisik) seperti yang kami lakukan dan Anda ingin mengunduh PDF tanpa memuat ulang halaman, fungsi berikut berfungsi untuk kami:

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

Karena target = "pdf-download-output" , respons ditulis ke dalam iframe dan oleh karena itu tidak ada pemuatan ulang halaman yang dijalankan, tetapi pdf-response-stream dikeluarkan di browser sebagai unduhan.

George Maharis
sumber
maaf, tapi bagaimana Anda mendapatkan nilai transferData?
Kate