2010-02-02 14 views
46

Tengo un enlace 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.POST al servidor, recibir PDF, entregar al usuario w/jQuery

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

+0

Define 'its default thing'. –

+0

que sería lo que el navegador normalmente hace con una respuesta con el tipo de contenido establecido en PDF. Abra un lector, solicite una ubicación de descarga, etc. – bryanvick

Respuesta

31

No es necesario jQuery en absoluto. Sólo enviar su entrada a través de una forma normal, y en el lado del servidor, agregar el encabezado HTTP

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

el navegador haga su trabajo por defecto.

Como alternativa, 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é del PDF. Asegúrese de incluir el encabezado Content-Disposition, como se indica arriba.

+5

Puede que no necesite, pero a veces lo hace;) –

50

Tome un vistazo a - jQuery Plugin for Requesting Ajax-like File Downloads

Todo el plugin se trata sólo de 30 líneas de código (incluyendo comentarios).

La llamada es bastante similar a la llamada jquery ajax.

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

Por supuesto, usted tiene que configurar los encabezados de tipo de contenido y Content-Disposition en el lado servidor como si se tratara de cualquier descarga.

en Java que haría algo como esto

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

Esta fue la respuesta, pero no tengo suficiente reputación para votarla. Sabía que estaba pensando en el problema de la manera incorrecta. – bryanvick

+0

Gracias, nuevo aquí. – bryanvick

-1

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 variantes debe instalar Acrobat reader para ver el pdf pero también puedes usar FlashPaper :)

+0

La respuesta está lejos del contexto de la pregunta, y no se proporcionan detalles –

3

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 desde Tengo un objeto complejo y una matriz de objetos para pasar como mis criterios de búsqueda/datos de filtro. 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 += "<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'); 

Probablemente no hace mucha diferencia, pero para proporcionar un cierto contexto, tengo una interfaz de usuario javascript y nocaut poner 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.

0

¡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 su código, veo que desea 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).

+0

Ah después de leer su código, veo que desea enviar una serie 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? –

+0

Tu código se ve bien y probé algo similar y parece que funciona. ¿Puedes describir qué parte no funcionó para ti? –

2

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 i enviar el objeto de datos como un parámetro con el contenido JSON (yo uso aquí AngularJS pero debería funcionar con jQuery.param() también.)

Javascript:

$('<form target="_blank" action="' + appConstants.restbaseurl + '/print/pdf" method="POST">' + 
    "<input name='data' value='" + angular.toJson($scope.versicherung) + "' />" + 
    '</form>').appendTo('body').submit().remove(); 

en el lado del servidor se utiliza un CXF REST Service con un JACKSON Proveedor:

primavera 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> 

en el controlador I Extracto ed el parámetro y lo convirtió de nuevo a un Pojo Java:

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 el PRINTSERVICE construyo el binario pdf 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 puede no maneja las URLs de datos) y en tabletas y teléfonos inteligentes y no tiene problemas con popupblockers

+0

No tiene forma de decirle al usuario que la descarga se ha completado, ¿o sí? – crush

+0

no, hasta que use algunos trucos adicionales, como p.configuración de cookies –

1

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 , agregarlo al cuerpo de la página, enviarlo y quitándolo

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

    $("<form>") 
     .attr("action", url) 
     .attr("method", "post") 
     .append(
      $("input") 
       .attr("type", "hidden") 
       .attr("name", "data") 
       .attr("value", encodedData) 
     ) 
     .appendTo("body") 
     .submit() 
     .remove(); 
}; 
Cuestiones relacionadas