Download and open PDF file using Ajax
I have an action class that generates a 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
through an Ajax call. I don't know the way to deliver this stream to browser. I tried a few things but nothing worked.
$.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));
}
});
The above gives the error:
Ваш браузер отправил запрос, который этот сервер не может понять.
18 ответов
Вам не обязательно нужен Ajax для этого. Просто <a>
ссылка достаточно, если вы установите content-disposition
в 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>
Обновленный ответ с использованием download.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>
Простая форма с двумя входными текстами, одним выделенным и кнопочным элементом.
Источник страницы 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. Это многоразово. Надеюсь, это поможет.
Для меня работал следующий код, так как функция сервера извлекает 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();
}
});
Чтобы исправить пустую проблему 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);
}
});
Вы можете использовать этот плагин.
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
);
Это сработало для меня. Нашел этот плагин здесь
Что касается ответа, данного Mayur Padshala это правильная логика для загрузки файла 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();
}
Следующий код работал для меня
//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);
Надеюсь, это сэкономит вам несколько часов и избавит от головной боли. Мне потребовалось некоторое время, чтобы понять это, но выполнение регулярного запроса $.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);
Этот фрагмент предназначен для пользователей 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 () {
});
Я нашел решение, которое решило эту проблему для меня (пустой pdf при использовании jquery ajax). Я нашел это волшебное решение здесь: https://www.py4u.net/discuss/904599 (ответ 2), и оно включает добавление xhrFields к вашему вызову ajax:
xhrFields: {
responseType: 'blob'
}
Мой рабочий пример:
$.ajax({
url: "myUrl",
type: 'GET',
headers: {"token": mySecurityToken},
xhrFields: {
responseType: 'blob'
},
data: {id: myId}
}).done(function( data, statusText, xhr ) {
var filename = "";
var disposition = xhr.getResponseHeader("Content-Disposition");
if (disposition && (disposition.indexOf("attachment") !== -1) || disposition.indexOf("filename") !== -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([data], {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
}
})
Я надеюсь, что это решит эту неприятную проблему для многих из вас.
Создайте скрытый iframe, затем в своем Ajax-коде выше:
URL: document.getElementById('myiframeid').src = your_server_side_url
,
и удалите window.open(response);
Вы должны сделать это с Ajax? Может ли быть возможность загрузить его в iframe?
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 выводится в браузере как загрузка.
100% OK для всех типов файлов
// download the file
var link = document.createElement('a'),
filename = fname;
link.href = URL.createObjectURL(data);
link.download = filename;
link.click();
Лучше всего использовать якорь или форму с предоставленной ссылкой, но вам нужно выполнить проверку или, в других случаях, используя jquery, лучше всего добавить форму и отправить ее с помощью jquery (не забудьте установить размещение вашего запроса в виде вложения на стороне сервера).
<form id="pdf-form" action="/link_to/download_your.pdf" accept-charset="UTF-8" method="get">
<input type="hidden" name="data" id="data" value="your data"></form>
а также
<a href="javascript:void(0);" id="pdf">Download my Pdf</a>
затем в jquery
$('#pdf').click(function () {
// your data if it json do it like this JSON.stringify(your_data_as_json)
$('#data').val(data);
$('#pdf-form').submit();
})