Ajax kullanarak PDF dosyasını indirin ve açın


101

PDF oluşturan bir eylem sınıfım var. contentTypeUygun şekilde ayarlanır.

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

Bunu action bir Ajax aramasıyla arıyorum. Bu akışı tarayıcıya iletmenin yolunu bilmiyorum. Birkaç şey denedim ama hiçbir şey işe yaramadı.

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

Yukarıdakiler hatayı verir:

Tarayıcınız bu sunucunun anlayamadığı bir istek gönderdi.

Yanıtlar:


40

Bunun için Ajax'a ihtiyacınız yok. Sadece bir <a>ayarladığınız takdirde bağlantı yeterlidir content-dispositioniçin attachmentsunucu tarafı kodu. Bu şekilde ana sayfa açık kalacaktır, eğer asıl endişeniz buysa (aksi halde neden Ajax'ı gereksiz yere seçtiniz?). Ayrıca, bunu eşzamansız olarak güzelce halletmenin bir yolu yok. PDF, karakter verisi değildir. İkili veri. Böyle şeyler yapamazsınız $(element).load(). Bunun için tamamen yeni bir istek kullanmak istiyorsunuz . Bunun <a href="pdfservlet/filename.pdf">pdf</a>için mükemmel bir şekilde uygundur.

Sunucu tarafı koduyla size daha fazla yardımcı olmak için, kullanılan dil hakkında daha fazla bilgi vermeniz ve kod denemelerinden bir alıntı göndermeniz gerekir.


7
Bir kez daha: Bunun için Ajax'a ihtiyacınız yok . Sadece bela istiyor. PDF ikili verilerdir, HTML veya JSON gibi karakter verileri değildir.
BalusC

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

5
Bazı tarayıcılarda window.open açık ve boş kalacaktır, bu da kullanıcılar için can sıkıcı olabilir. Bu nedenle, bunun için window.open'ı da KULLANMAYIN. Olarak content-dispositionayarlanmışsa attachment, sadece bir Save asdiyalog alacaksınız . Üst sayfa değişmeden kalacaktır. Sadece <a href="pdfservlet/filename.pdf">pdf</a>veya a <form action="pdfservlet/filename.pdf"><input type="submit"></form>fazlasıyla yeterli.
BalusC

5
Sınırlı bir URL uzunluğu var. Ve yazar POST hakkında soru soruyor.
Edward Olamisan

3
@EdwardOlamisan ile aynı fikirdeyim, yazar POSTverilere çalıştığı için bu doğru bir cevap değil .
adamj

123

İşte bunu nasıl çalıştırdım

$.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 kullanılarak güncellenmiş yanıt

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


30
Chrome üzerinde çalışıyor mu? Sadece boş bir pdf görebiliyorum.
Tarun Gupta

1
Evet, tüm modern tarayıcılarda çalışır. Boş bir pdf görürseniz, ajax url'sini yeni bir sekmede çalıştırmayı deneyin. Orada da boş bir ekran görürseniz, pdf'nin kendisinde bir sorun olabilir. İndirilen dosyada değil de orada bir pdf dosyası görürseniz, e-postamda bana bildirin. :)
Mayur Padshala

5
Bu (bağlantı elemanı) aslında IE 11, Edge ve Firefox'ta benim için işe yaramadı. başarıyı sadece "window.open (URL.createObjectURL (blob))" kullanarak değiştirmek işe yaradı.
JimiSweden

3
pdf dosyası indirildi ancak içerik mevcut değil. [] baytını sunucu tarafında kaydettim ve pdf içeriği mevcut. lütfen önerin.
Awanish Kumar

4
boş pdf dosyası indirilir.
Farukh

31

Geçmişteki yanıtların hiçbirinin orijinal posterin sorununu tespit ettiğini gerçekten düşünmüyorum. Gönderen POST verisi vermeye ve yanıt olarak bir indirme almaya çalışırken hepsi bir GET isteği olduğunu varsayar.

Daha iyi bir cevap ararken, Ajax Benzeri Dosya İndirmeleri İstemek için bu jQuery Eklentisini bulduk .

"Kalbinde", verilen verileri giriş alanları olarak içeren "geçici" bir HTML formu oluşturur. Bu form belgeye eklenir ve istenen URL'ye gönderilir. Hemen ardından form tekrar kaldırılır:

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

Güncelleme Mayur'un cevabı, bahsettiğim jQuery eklentisine kıyasla oldukça umut verici ve çok basit görünüyor.


9

Bu sorunu bu şekilde çözüyorum.
Jonathan Amend'in bu yazıdaki cevabı bana çok yardımcı oldu.
Aşağıdaki örnek basitleştirilmiştir.

Daha fazla ayrıntı için, yukarıdaki kaynak kodu bir JQuery Ajax isteği (GET, POST, PUT vb.) Kullanarak bir dosya indirebilir . Ayrıca, parametreleri JSON olarak yüklemeye ve içerik türünü application / json (varsayılanım) olarak değiştirmeye yardımcı olur .

Html kaynağı:

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

İki giriş metni, bir seçim ve bir düğme öğesi içeren basit bir form.

Javascript sayfa kaynağı:

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

Düğme tıklandığında basit bir olay. Bir AjaxDownloadFile nesnesi oluşturur. AjaxDownloadFile sınıf kaynağı aşağıdadır.

AjaxDownloadFile sınıf kaynağı:

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 kitaplığıma eklemek için bu sınıfı oluşturdum. Yeniden kullanılabilir. Umarım yardımcı olur.


2
Blobnesne IE10 + 'da desteklenmektedir.
ezmek

Ben ayarlamak zorunda responseTypeiçin XHR'nin ait arraybufferveya blobbunun için işe. (Aksi takdirde, bu harika çalışıyor.)
tjklemz

Bende de aynı soru vardı. "Sadece bir bağlantı yap" şeklinde yanıt veren tüm insanlar OP'ye yardımcı olmuyor. İçeriğiniz dinamikse ve gideceğiniz bağlantı dinamikse, hepsini sorgulamalısınız ... benim için cevap, sayfaya tüm gizli girişleri (kullanıcıya gösterilmemiştir) içeren bir form koymak ve sonra doldurun ve jquery ile gönderin. Harika çalışıyor.
Scott

Bu harika bir cevap, ancak nedense, boş PDF'yi kırmaya devam ediyorum. Çözemiyorum. API aracılığıyla aynı bayt kümesini döndürdüğümde - sorun değil, bu yüzden MVC yanıtıyla ilgili bir şey. FileResult yanıt türünü kullanıyorum: File (bytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
Jurijs Kastanovs

Açıklama: URL'yi adres çubuğundan açarsanız - dosya doğru bir şekilde açılır. Dosyayı almak için AJAX + blob kullanırsam - dosya bozuk.
Jurijs Kastanovs

7

Benim için işe yarayan şey, sunucu işlevi alırken aşağıdaki koddur 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();
                }
});

Bu yorum ve en yeni Chrome
Loredra L

6

Bir form oluşturan ve gönderen ve ardından sayfadan kaldıran bu eklentiyi kullanabilirsiniz.

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

Bu benim için çalıştı. Bu eklentiyi burada buldum


Bu eklenti sadece bir form oluşturur ve gönderir, ardından sayfadan kaldırır. (herkes merak varsa)
ezmek

4

Aşağıdaki kod benim için çalıştı

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

PDF gibi akış verilerini almak için istek sonrası boş PDF sorununu düzeltmek için, istekte yanıt türünü 'arraybuffer' veya 'blob' olarak eklememiz gerekir.

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

Mayur Padshala tarafından verilen cevapla ilgili olarak bu, bir pdf dosyasını ajax aracılığıyla indirmek için doğru mantıktır, ancak diğerlerinin yorumlarda bildirdiği gibi, bu çözüm aslında boş bir pdf indirmektedir.

Bunun nedeni bu kabul cevap açıklanmıştır soru : jQuery henüz bazı HTML5 XHR v2 yeteneklerini uygulamak bu gelişmenin görmez olarak, AJAX isteklerini kullanarak ikili veri yükleme bazı sorunları vardır isteği ve bu tartışma .

Yani HTMLHTTPRequestkodu kullanmak şu şekilde görünmelidir:

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

gizli bir iframe oluşturun, ardından yukarıdaki ajax kodunuzda:

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

ve kaldır window.open(response);


Bu çözüm harika çalıştı. Dosyayı curl aracılığıyla getiren bir hizmete curl çağrısı yapan bir sunucu tarafı komut dosyasını arıyorum. Bu, bir yükleme gif'i bırakabildiğim ve istek bağlantısını devre dışı bırakabildiğim için harika çalışıyor.
eggmatters

1
Bu çözüm, orijinal gönderideki gibi POST istekleri için değil GET istekleri için çalışır.
chiccodoro

2

Bu kod parçası, aynı sorunla karşılaşacak olan açısal js kullanıcıları içindir. Yanıt dosyasının programlanmış bir tıklama olayı kullanılarak indirildiğini unutmayın. Bu durumda, başlıklar dosya adı ve içerik / tür içeren sunucu tarafından gönderilmiştir.

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

});

Lütfen cevabınız için bir açıklama yazın.
Gufran Hasan

2

Umarım bu sizi birkaç saat kurtarır ve baş ağrısından kurtarır. Bunu anlamam biraz zaman aldı, ancak normal $ .ajax () isteğini yapmak PDF dosyamı mahvetti, ancak adres çubuğundan istemek mükemmel çalıştı. Çözüm şuydu:

Download.js'yi dahil et: http://danml.com/download.html

Daha sonra $ .ajax () isteği yerine XMLHttpRequest kullanın.

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

Aynı problem için birkaç saat harcadım. PDF yazı tipi doğru yüklenmeyecek. Teşekkür ederim!
Blodhgard

1

Ajax ile yapmak zorunda mısın? Bir iframe'e yüklemek mümkün olamaz mı?


1
Bunun Ajax ile yapılıp yapılamayacağını kontrol ediyorum. Teknik olarak imkansız ya da yetersiz bir yaklaşımsa, diğer yaklaşımlara geçerim.
Nayn

1

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

Bizim yaptığımız gibi dosya akışı ile çalışmanız gerekiyorsa (fiziksel olarak kaydedilmiş PDF yok) ve PDF'yi sayfayı yeniden yüklemeden indirmek istiyorsanız, aşağıdaki işlev bizim için çalışır:

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" nedeniyle , yanıt iframe'e yazılır ve bu nedenle hiçbir sayfa yeniden yüklenmez, ancak pdf-yanıt akışı tarayıcıda bir indirme olarak çıktı olarak alınır.


üzgünüm ama transferData değerini nasıl elde edersiniz?
Kate
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.