POST на сервер, получить PDF, доставить пользователю w/ jQuery
У меня есть ссылка, по которой пользователь нажимает, чтобы получить PDF. В jQuery я создаю ajax-вызов POST на сервер, чтобы получить PDF. PDF приходит ко мне с правильными заголовками содержимого и т. Д., Которые обычно приводят к тому, что браузер открывает плагин Reader или позволяет пользователю сохранить PDF.
Поскольку я получаю PDF с вызовом ajax, я не уверен, что делать с данными, полученными в обратном вызове OnSuccess. Как я могу передать данные, которые я получаю, в браузер и позволить ему делать то же, что и по умолчанию, с ответом в формате PDF?
7 ответов
Вам не нужен JQuery вообще. Просто отправьте POST через форму в обычном режиме, а на стороне сервера добавьте заголовок HTTP
Content-Disposition: attachment; filename="whatever.pdf"
Браузер сделает свою вещь по умолчанию.
В качестве альтернативы, если вы хотите быть более осторожным в сообщении о любых ошибках, которые могут возникнуть во время создания PDF, вы можете сделать это. Разместите ваши параметры на вашем сервере с помощью jQuery. На сервере сгенерируйте двоичный контент и кэшируйте его где-нибудь в течение нескольких минут, к которому можно получить доступ с помощью ключа, который вы указали в сеансе пользователя, и верните "успешный" ответ Ajax на вашу страницу (или, если произошла ошибка, верните "ошибка" ответ). Если страница возвращает ответ об успешном завершении, она может сразу сделать что-то вроде:
window.location = "/get/my/pdf";
Затем сервер возвращает кэшированное содержимое PDF. Не забудьте включить заголовок Content-Disposition, как указано выше.
Взгляните на плагин jQuery для запроса Ajax-подобных загрузок файлов
Целый plugin
это всего около 30 строк кода (включая комментарии).
Вызов довольно похож на вызов jquery ajax.
$.download('/export.php','filename=myPDF&format=pdf&content=' + pdfData );
Конечно, вы должны установить заголовки content-type и Content-Disposition на стороне сервера, как при любой такой загрузке.
В Java я бы сделал что-то вроде этого
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename="exported.pdf");
Ответ с упоминанием "Плагин jQuery для запроса Ajax-подобных загрузок файлов" заставил меня двигаться в правильном направлении, но это не сработало полностью для моей ситуации, поскольку у меня есть сложный объект и массив объектов, которые нужно передать в качестве критериев поиска / фильтровать данные. Я решил поделиться своим кодом на случай, если кто-то еще столкнется с этой ситуацией.
$.download = function (url, data, method) {
if (url && data) {
//convert the data object into input HTML fields
var inputs = '';
var convertToInput = function (key, keyStr, obj) {
if (typeof obj === 'undefined') {
return;
} else if (typeof obj === "object") {
for (var innerKey in obj) {
if (obj.hasOwnProperty(innerKey)) {
var innerKeyStr = '';
if (keyStr === '') {
innerKeyStr = innerKey.toString();
} else {
innerKeyStr = keyStr + "[" + innerKey.toString() + "]";
}
convertToInput(innerKey, innerKeyStr, obj[innerKey]);
}
}
return;
} else if ($.isArray(obj)) {
obj.forEach(function (item) {
convertToInput(key, keyStr + "[]", item);
});
return;
}
inputs += "<input type='hidden' name='" + keyStr + "' value='" + obj + "' />";
};
convertToInput(null, '', data);
//send request
jQuery('<form action="' + url + '" method="' + (method || 'post') + '">' + inputs + '</form>').appendTo('body').submit().remove();
};
};
$.download('/api/search?format=csv', searchData, 'POST');
Это, вероятно, не имеет большого значения, но для обеспечения некоторого контекста у меня есть пользовательский интерфейс javascript и knockout, вызывающий WebAPI, MVC4 и nHibernate. Часть 'format=csv' строки запроса запускает MediaTypeFormatter для преобразования возвращаемых моделей в тип файла CSV. Если я отключу это, то получу модели обратно из API и смогу заполнить сетку Slick для отображения.
У меня была такая же проблема, но сверху использовать RESTFUL webservice
для этого и есть сложный объект данных, который я должен опубликовать.
Мое решение: как и плагин jQuery, я создаю временную формуляру и отправляю ее. Но я отправляю объект данных как параметр с содержимым json (я использую здесь AngularJS
но это должно работать с jQuery.param()
тоже.)
Javascript:
$('<form target="_blank" action="' + appConstants.restbaseurl + '/print/pdf" method="POST">' +
"<input name='data' value='" + angular.toJson($scope.versicherung) + "' />" +
'</form>').appendTo('body').submit().remove();
на стороне сервера мы используем CXF REST Service
с JACKSON
Поставщик:
Spring Config:
<jaxrs:server id="masterdataService" address="/">
<jaxrs:serviceBeans>
<ref bean="printRestServiceBean" />
</jaxrs:serviceBeans>
<jaxrs:providers>
<bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider" />
<bean class="de.controller.ExceptionHandler" />
</jaxrs:providers>
</jaxrs:server>
в контроллере я извлек параметр и преобразовал его обратно в Java Pojo:
package de.controller;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
@Path(Constants.PRINT_PATH)
@Consumes({ MediaType.APPLICATION_JSON, "application/x-www-form-urlencoded"})
@Produces("application/pdf; charset=UTF-8")
public class PrintRestController {
@Autowired
private PrintService printService;
@POST
@Produces("application/pdf")
@Path("/pdf")
public Response getPDF(@FormParam("data") String data) {
return printService.getPDF(json2Versicherung(data));
}
private Versicherung json2Versicherung(String data) {
Versicherung lVersicherung = null;
try {
ObjectMapper mapper = new ObjectMapper();
lVersicherung = mapper.readValue(data, Versicherung.class);
} catch(Exception e) {
LOGGER.error("PrintRestController.json2Versicherung() error", e);
}
return lVersicherung;
}
}
в PrintService я строю двоичный файл PDF и ответ:
@Override
public Response getPDF(Versicherung pVersicherung) {
byte[] result = ... //build the pdf from what ever
ResponseBuilder response = Response.ok((Object) result);
response.header("Content-Disposition", "inline; filename=mypdf.pdf");
return response.build();
}
Это решение работает для всех браузеров (даже для IE9, которые не могут обрабатывать URL-адреса данных), а также для планшетов и смартфонов, и у него нет проблем с всплывающими окнами
Плагин jQuery для запроса Ajax-подобных загрузок файлов - это, по сути, создание формы, добавление данных публикации в виде скрытых полей, добавление их в тело страницы, отправка и удаление.
В моем случае у меня не было формы, только часть данных для публикации, как это было. Это сделано для следующего решения. На стороне сервера я могу получить данные, просто прочитав параметр "data" из запроса и расшифровав его по URI.
function postAndDownload(url, data) {
encodedData = encodeURIComponent(data);
$("<form>")
.attr("action", url)
.attr("method", "post")
.append(
$("input")
.attr("type", "hidden")
.attr("name", "data")
.attr("value", encodedData)
)
.appendTo("body")
.submit()
.remove();
};
Я не понимаю, почему вы хотите ajax-запрос к URL-адресу загрузки файла! Но если это больше похоже на то, как клиент сам генерирует некоторый контент для загрузки - используйте данные URI. Прекрасно работает для Chrome и Firefox 20+. Safari и IE нет! Если Flash разрешен, вы можете использовать загрузчик.
Ах, после прочтения вашего кода, я вижу, вы хотите отправить кучу параметров. Ну, если строка запроса не становится слишком длинной (IE8- имеет ограничение 2083), почему бы просто не использовать привязку с правильным URL?
$('a.export-csv').click( function (evt){
linkEl.attr('href','/export?' + encodeURIComponent(formQueryString()));
return true;
});
Выше можно изменить URL-адрес до того, как произойдет событие по умолчанию (щелчок).
Я думаю, что лучше всего будет создать временный pdf-файл в папке загрузок, а затем загрузить файл с помощью всплывающего окна, содержащего iframe. Chrome загрузит его мгновенно, но я полагаю, что для других вариантов необходимо установить Acrobat Reader для просмотра PDF но опять же вы можете использовать FlashPaper тоже:)