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

98

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

Вышеуказанное дает ошибку:

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

Найн
источник

Ответы:

37

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

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

BalusC
источник
7
Еще раз: для этого вам не нужен Ajax. Это просто напрашивается на неприятности. PDF - это двоичные данные, а не символьные данные, такие как HTML или JSON.
BalusC 04
3
var url = contextPath + "/xyz/blahBlah.action"; url + = url + "?" + параметры; попробуйте {var child = window.open (url); child.focus (); } catch (e) {}
Найн
5
В некоторых браузерах window.open остается открытым и пустым, что может раздражать конечных пользователей. Поэтому НЕ используйте для этого window.open. Если content-dispositionустановлено значение attachment, вы просто получите Save asдиалог. Родительская страница останется без изменений. Просто <a href="pdfservlet/filename.pdf">pdf</a>или <form action="pdfservlet/filename.pdf"><input type="submit"></form>более чем достаточно.
BalusC 04
5
Длина URL-адреса ограничена. А автор спрашивает про POST.
Эдвард Оламисан
3
Согласитесь с @EdwardOlamisan, это неправильный ответ, поскольку автор пытался получить POSTданные.
adamj 05
123

Вот как у меня это работает

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

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

Маюр Падшала
источник
29
На хроме работает? Я вижу только пустой PDF-файл.
Тарун Гупта
1
Да, он работает во всех современных браузерах. Если вы видите пустой PDF-файл, попробуйте запустить URL-адрес ajax на новой вкладке. Если вы видите пустой экран и там, возможно, проблема связана с самим PDF-файлом. Если вы видите PDF-файл там, а не в загруженном файле, сообщите мне об этом по электронной почте. :)
Маюр Падшала
5
Этот (элемент привязки) на самом деле не работал у меня в IE 11, Edge и Firefox. изменение успеха на простое использование «window.open (URL.createObjectURL (blob))» сработало.
JimiSweden
3
pdf файл загружен, но его содержимое недоступно. У меня есть save byte [] на стороне сервера, и содержимое pdf доступно. пожалуйста, предложите.
Аваниш Кумар 01
4
пустой файл pdf загружен.
Фарух
31

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

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

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

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

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

chiccodoro
источник
9

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

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

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

Источник страницы 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 приведен ниже.

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

Я создал этот класс, чтобы добавить его в свою библиотеку JS. Он многоразовый. Надеюсь, это поможет.

Джордж Сиггуроглу
источник
2
Blobобъект поддерживается в IE10 +.
раздавить
Мне пришлось установить responseTypexhr на arraybufferили, blobчтобы это работало. (В противном случае это отлично работает.)
tjklemz
У меня был точно такой же вопрос. Все люди, отвечающие «просто сделайте ссылку», не помогают OP. Если ваш контент динамический, а ссылка, по которой вы собираетесь, является динамической, вам нужно выполнить jquery все это ... ответ для меня заключался в том, чтобы поместить форму на страницу со всеми скрытыми входами (не отображается для пользователя) и затем заполните его и отправьте с помощью jquery. Прекрасно работает.
Скотт
Это отличный ответ, но по какой-то причине я все время получаю сломанный пустой PDF-файл. Не могу понять. Когда я возвращаю тот же набор байтов через API - это нормально, значит, это как-то связано с ответом MVC. Я использую тип ответа FileResult: File (байты, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
Jurijs
Уточнение: если открыть URL через адресную строку - файл открывается правильно. Если я использую AJAX + blob для получения файла - файл имеет неправильный формат.
Jurijs
7

У меня сработал следующий код, так как функция сервера получает 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
источник
Это отлично работает для меня во время этого комментария и новейшего Chrome
Loredra L
6

Вы можете использовать этот плагин, который создает форму и отправляет ее, а затем удаляет ее со страницы.

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

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

Иджас Аминудин
источник
Этот плагин просто создает форму и отправляет ее, а затем удаляет ее со страницы. (если кому-то было интересно)
Crush
4

Следующий код работал у меня

//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
источник
4

Чтобы исправить проблему с пустым PDF-файлом в почтовом запросе для получения потоковых данных, таких как PDF, нам нужно добавить в запрос тип ответа как arraybuffer или blob

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

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

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

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

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

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

Калхат
источник
Это решение сработало как шарм. Я вызываю сценарий на стороне сервера, который выполняет вызов curl к службе, которая извлекает файл через curl. Это отлично работает, так как я могу сбросить загружаемый гиф и отключить ссылку запроса.
eggmatters
1
Это решение работает для запросов GET, а не для запросов POST, как в исходном сообщении.
chiccodoro
2

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

});
Гихан Сандару
источник
Напишите, пожалуйста, пояснение к вашему ответу.
Гуфран Хасан
1

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

Эмиль Викстрём
источник
1
Я проверяю, можно ли это сделать с помощью Ajax. Если это технически невозможно или неполноценный подход, я бы переключился на другие подходы.
Nayn
1

Надеюсь, это сэкономит вам несколько часов и избавит от головной боли. Мне потребовалось некоторое время, чтобы понять это, но выполнение обычного запроса $ .ajax () испортило мой PDF-файл, а запрос его через адресную строку работал отлично. Решение было такое:

Включите download.js: http://danml.com/download.html

Затем используйте XMLHttpRequest вместо запроса $ .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

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

Джордж Махарис
источник
извините, а как получить значение transferData?
Кейт