Скачать и открыть PDF файл с помощью Ajax


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

Я называю это 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 74

12 ответов:

вам не обязательно нужен Ajax для этого. Просто <a> ссылка достаточно, если вы установите content-disposition to attachment в коде на стороне сервера. Таким образом, родительская страница просто останется открытой, если это было вашей главной заботой (почему вы без необходимости выбрали Ajax для этого в противном случае?). Кроме того, нет никакого способа справиться с этим красиво асинхронно. 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()

обновление ответ Маюра выглядит довольно многообещающим и очень простым по сравнению с плагином 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>  

простая форма с двумя входными текстами, одним выбором и элементом кнопки.

The 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 находится ниже.

The AjaxDownloadFile class источник:

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? Co не может ли это быть возможность загрузить его в iframe?

этот фрагмент предназначен для пользователей angular 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 () {

});

относительно ответа, данного 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-ответ-поток выводится в браузере в качестве загрузки.