POST al servidor, recibir PDF, entregar al usuario w / jQuery

Tengo un enlace en el que el usuario hace clic para obtener un PDF. En jQuery, creo una llamada POST ajax al servidor para obtener el PDF. El PDF me llega con los encabezados de contenido correctos, etc., que normalmente harían que el navegador abriera el plugin Reader o permitiría al usuario guardar el PDF.

Como recibo el PDF con una llamada ajax, no estoy seguro de qué hacer con los datos que obtengo en la callback de OnSuccess. ¿Cómo puedo dar los datos que recibo al navegador y permitir que haga lo predeterminado con la respuesta en PDF?

No necesitas jQuery en absoluto. Simplemente envíe su POST a través de un formulario normalmente, y en el lado del servidor, agregue el encabezado HTTP

 Content-Disposition: attachment; filename="whatever.pdf" 

El navegador hará lo predeterminado.

Alternativamente, si desea tener más cuidado al informar cualquier error que pueda ocurrir durante la generación de PDF, puede hacerlo. PUBLIQUE sus parámetros a su servidor con jQuery. En el servidor, genere el contenido binario y almacénelo en la memoria en algún lugar durante unos minutos, accesible a través de una clave que ingrese en la sesión del usuario y devuelva una respuesta Ajax “exitosa” a su página (o si hubo un error, devuelva un respuesta de “error”). Si la página obtiene una respuesta exitosa, inmediatamente puede hacer algo como:

 window.location = "/get/my/pdf"; 

El servidor luego devuelve el contenido en caché PDF. Asegúrese de incluir el encabezado Content-Disposition, como se indica arriba.

Eche un vistazo a: jQuery Plugin para solicitar descargas de archivos tipo Ajax

El plugin completo tiene solo 30 líneas de código (incluidos los comentarios).

La llamada es bastante similar a la llamada jquery ajax.

 $.download('/export.php','filename=myPDF&format=pdf&content=' + pdfData ); 

Por supuesto, debe establecer los encabezados de contenido y disposición de contenido en el servidor como lo haría con cualquier descarga de este tipo.

En Java, haría algo como esto

 response.setContentType("application/pdf"); response.setHeader("Content-Disposition", "attachment; filename="exported.pdf"); 

La respuesta que menciona “jQuery Plugin para solicitar descargas de archivos tipo Ajax” me llevó en la dirección correcta, pero no funcionó por completo para mi situación ya que tengo un objeto complejo y una serie de objetos para pasar como mis criterios de búsqueda / filtrar datos Pensé que compartiría mi código por si alguien más se encuentra con esta situación también.

 $.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 += ""; }; convertToInput(null, '', data); //send request jQuery('
' + inputs + '
').appendTo('body').submit().remove(); }; }; $.download('/api/search?format=csv', searchData, 'POST');

Probablemente no marque la diferencia, pero para proporcionar un contexto, tengo una llamada de IU javascript y knockout en WebAPI, MVC4 y nHibernate. La parte ‘format = csv’ de la cadena de consulta activa MediaTypeFormatter para convertir los modelos devueltos en un tipo de archivo CSV. Si lo dejo, recupero los modelos de la API y puedo llenar una grilla Slick para mostrar.

Tuve el mismo problema, pero en la parte superior uso un RESTFUL webservice para esto y tengo un objeto de datos complejo que debo publicar.

Mi solución: como el jQuery Plugin, construyo un temp form y lo presento. Pero envío el objeto de datos como un parámetro con contenido json (uso aquí AngularJS pero debería funcionar con jQuery.param() también).

Javascript:

 $('
' + "" + '
').appendTo('body').submit().remove();

en el lado del servidor usamos un CXF REST Service con un proveedor JACKSON :

Spring Config:

          

en el controlador, extraje el parámetro y lo convertí de nuevo a 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; } } 

en PrintService construyo el pdf binario y la respuesta:

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

Esta solución funciona para todos los navegadores (incluso para IE9, que no puede manejar url de datos) y en tabletas y teléfonos inteligentes, y no tiene problemas con popupblockers.

El complemento jQuery para solicitar descargas de archivos tipo Ajax es, esencialmente, crear un formulario, agregar los datos de la publicación como campos ocultos, agregarlos al cuerpo de la página, enviarlos y eliminarlos.

En mi caso, no tenía un formulario, solo una porción de datos para publicar como estaba. Eso hizo para la siguiente solución. En el lado del servidor, puedo obtener los datos simplemente leyendo el parámetro “datos” de la solicitud y decodificando el URI.

 function postAndDownload(url, data) { encodedData = encodeURIComponent(data); $("
") .attr("action", url) .attr("method", "post") .append( $("input") .attr("type", "hidden") .attr("name", "data") .attr("value", encodedData) ) .appendTo("body") .submit() .remove(); };

¡No entiendo por qué quieres una solicitud de AJAX para una URL de descarga de archivos! Pero si el cliente mismo genera algo de contenido para descargar, use un uri de datos. Funciona perfectamente para Chrome y Firefox 20+. Safari e IE NO! Si se permite Flash, puedes usar el descargador.

Ah después de leer tu código, veo que quieres enviar un montón de parámetros. Bueno, a menos que la cadena de consulta sea demasiado larga (IE8- tiene un límite de 2083) ¿por qué no simplemente usar un ancla con la URL adecuada?

  $('a.export-csv').click( function (evt){ linkEl.attr('href','/export?' + encodeURIComponent(formQueryString())); return true; }); 

Lo anterior le permite cambiar la URL antes de que ocurra el evento predeterminado (el clic).

Creo que lo mejor será crear un archivo pdf temp en la carpeta de descargas y luego cargar el archivo usando pop-up con un iframe … Chrome lo cargará instantáneamente, pero supongo que para otras versiones, Acrobat Reader debe estar instalado para ver el pdf pero de nuevo puedes usar FlashPaper también 🙂