Загрузка и открытие PDF-файла с помощью Ajax

у меня есть класс действий, который генерирует PDF-файл. The 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;
   }
}

Я называю это action через вызов Ajax. Я не знаю, как доставить этот поток в браузер. Я попробовал несколько вещей, но ничего не получалось.

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

выше дает ошибку:

Ваш браузер послал запрос, что этот сервер не мог понять.

12 ответов


вам не обязательно нужен Ajax для этого. Просто <a> ссылка достаточно, если вы установите content-disposition to attachment в коде на стороне сервера. Таким образом, родительская страница останется открытой, если это было вашей главной проблемой (почему вы без необходимости выбрали Ajax для этого в противном случае?). Кроме того, нет никакого способа справиться с этим красиво acynchronously. PDF-это не символьные данные. Это двоичные данные. Вы не можете делать такие вещи, как $(element).load(). Вы хотите использовать совершенно новый запрос для этого. За это <a href="pdfservlet/filename.pdf">pdf</a> идеально подходит.

чтобы помочь вам больше с кодом на стороне сервера, вам нужно будет рассказать больше об используемом языке и опубликовать выдержку попыток кода.


вот как я получил это работает

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

обновленный ответ с помощью скачать.js

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

Я действительно не думаю, что какой-либо из прошлых ответов заметил проблему оригинального плаката. Все они предполагают запрос GET, когда плакат пытался опубликовать данные и получить загрузку в ответ.

в процессе поиска лучшего ответа мы нашли это плагин jQuery для запроса Ajax-подобных загрузок файлов.

в своем " сердце "он создает" временную " HTML-форму, содержащую данные в качестве входных полей. Эта форма прилагается к документ и размещен по нужному URL-адресу. Сразу после этого форма удаляется, снова:

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

обновление ответ Mayur выглядит довольно многообещающим и очень простым по сравнению с плагином jQuery, о котором я упоминал.


вот как я решаю эту проблему.
Ответ Джонатана Аменде на этот пост мне очень помогли.
Приведенный ниже пример упрощен.

для получения более подробной информации приведенный выше исходный код может загрузите файл с помощью запроса 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>  

простая форма с двумя входными текст, один выберите и элемент 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 source.

на 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]*=(([""]).*?|[^;\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. Он многоразовый. Надеюсь, это поможет.


вы можете использовать этот плагин.

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

это сработало для меня. Нашел этот плагин здесь


что сработало для меня, это следующий код, так как функция сервера извлекает 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();
                }
});

создайте скрытый iframe, затем в вашем ajax-коде выше:

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

и удалить window.open(response);


вы должны сделать это с Ajax? Coouldn не будет возможности загрузить его в iframe?


этот фрагмент предназначен для пользователей angular js, которые столкнутся с той же проблемой, обратите внимание, что файл ответов загружается с помощью запрограммированного события click. В этом случае заголовки были отправлены сервером , содержащим имя файла и контент/тип.

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

});

относительно ответа, данного Padshala Маюр это правильная логика для загрузки pdf-файла через ajax, но, как сообщают другие в комментариях, это решение действительно загружает пустой pdf.

причина этого объясняется в принятом ответе this вопрос: 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();
}

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

Если вам нужно работать с файловым потоком (так что нет физически сохраненного 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 выводится в браузере как загрузка.