Tengo una aplicación web de una sola página basada en jquery. Se comunica con un servicio web RESTful a través de llamadas AJAX.
Estoy tratando de lograr lo siguiente:
- Envíe una POST que contenga datos JSON a una URL REST.
- Si la solicitud especifica una respuesta JSON, se devuelve JSON.
- Si la solicitud especifica una respuesta PDF / XLS / etc., se devuelve un binario descargable.
Tengo 1 y 2 trabajando ahora, y la aplicación jquery del cliente muestra los datos devueltos en la página web creando elementos DOM basados en los datos JSON. También tengo # 3 trabajando desde el punto de vista del servicio web, lo que significa que creará y devolverá un archivo binario si se le dan los parámetros JSON correctos. Pero no estoy seguro de la mejor manera de lidiar con el n. ° 3 en el código JavaScript del cliente.
¿Es posible recuperar un archivo descargable de una llamada ajax como esta? ¿Cómo consigo que el navegador descargue y guarde el archivo?
$.ajax({
type: "POST",
url: "/services/test",
contentType: "application/json",
data: JSON.stringify({category: 42, sort: 3, type: "pdf"}),
dataType: "json",
success: function(json, status){
if (status != "success") {
log("Error loading data");
return;
}
log("Data loaded!");
},
error: function(result, status, err) {
log("Error loading data");
return;
}
});
El servidor responde con los siguientes encabezados:
Content-Disposition:attachment; filename=export-1282022272283.pdf
Content-Length:5120
Content-Type:application/pdf
Server:Jetty(6.1.11)
Otra idea es generar el PDF y almacenarlo en el servidor y devolver JSON que incluye una URL al archivo. Luego, emita otra llamada en el controlador de éxito de ajax para hacer algo como lo siguiente:
success: function(json,status) {
window.location.href = json.url;
}
Pero hacer eso significa que necesitaría hacer más de una llamada al servidor, y mi servidor necesitaría construir archivos descargables, almacenarlos en algún lugar y luego limpiar periódicamente esa área de almacenamiento.
Debe haber una manera más simple de lograr esto. Ideas?
EDITAR: después de revisar los documentos por $ .ajax, veo que el tipo de datos de respuesta solo puede ser uno xml, html, script, json, jsonp, text
, por lo que supongo que no hay forma de descargar directamente un archivo usando una solicitud ajax, a menos que inserte el archivo binario al usar Esquema de URI de datos como se sugiere en la respuesta @VinayC (que no es algo que quiera hacer).
Entonces supongo que mis opciones son:
No use ajax y, en su lugar, envíe una publicación de formulario e incruste mis datos JSON en los valores del formulario. Probablemente necesitaría meterse con iframes ocultos y demás.
No use ajax y, en su lugar, convierta mis datos JSON en una cadena de consulta para crear una solicitud GET estándar y establecer window.location.href en esta URL. Es posible que necesite usar event.preventDefault () en mi controlador de clics para evitar que el navegador cambie de la URL de la aplicación.
Use mi otra idea anterior, pero mejorada con sugerencias de la respuesta @naikus. Envíe la solicitud AJAX con algún parámetro que permita que el servicio web sepa que se está llamando a través de una llamada ajax. Si se llama al servicio web desde una llamada ajax, simplemente devuelva JSON con una URL al recurso generado. Si se llama directamente al recurso, devuelva el archivo binario real.
Cuanto más lo pienso, más me gusta la última opción. De esta forma puedo recuperar información sobre la solicitud (tiempo de generación, tamaño del archivo, mensajes de error, etc.) y puedo actuar sobre esa información antes de comenzar la descarga. La desventaja es la administración adicional de archivos en el servidor.
¿Alguna otra forma de lograr esto? ¿Algún pros / contras de estos métodos que debo tener en cuenta?
fuente
url = 'http://localhost/file.php?file='+ $('input').val(); window.open(url);
La forma fácil de obtener los mismos resultados. Pon el encabezado en el archivo php. No es necesario enviar ningún ajax ni recibir solicitudesRespuestas:
La solución de letronje solo funciona para páginas muy simples.
document.body.innerHTML +=
toma el texto HTML del cuerpo, agrega el iframe HTML y establece el innerHTML de la página en esa cadena. Esto eliminará cualquier enlace de evento que tenga su página, entre otras cosas. Crea un elemento y úsaloappendChild
en su lugar.O usando jQuery
Lo que esto realmente hace: realizar una publicación en /create_binary_file.php con los datos en la variable postData; si esa publicación se completa con éxito, agregue un nuevo iframe al cuerpo de la página. Se supone que la respuesta de /create_binary_file.php incluirá un valor 'url', que es la URL desde la que se puede descargar el archivo PDF / XLS / etc generado. Agregar un iframe a la página que hace referencia a esa URL dará como resultado que el navegador promocione al usuario a descargar el archivo, suponiendo que el servidor web tenga la configuración de tipo mime adecuada.
fuente
+=
y actualizaré mi aplicación en consecuencia. ¡Gracias!Resource interpreted as Document but transferred with MIME type application/pdf
. Entonces también me advierte que el archivo puede ser peligroso. Si visito el retData.url directamente, no hay advertencias ni problemas.He estado jugando con otra opción que usa blobs. Me las arreglé para descargar documentos de texto y descargué archivos PDF (sin embargo, están dañados).
Usando la API blob podrás hacer lo siguiente:
Esto es IE 10+, Chrome 8+, FF 4+. Ver https://developer.mozilla.org/en-US/docs/Web/API/URL.createObjectURL
Solo descargará el archivo en Chrome, Firefox y Opera. Esto utiliza un atributo de descarga en la etiqueta de anclaje para forzar al navegador a descargarlo.
fuente
click
:window.URL.revokeObjectURL(link.href);
Conozco este tipo de edad, pero creo que se me ocurrió una solución más elegante. Tuve exactamente el mismo problema. El problema que tenía con las soluciones sugeridas era que todas requerían que el archivo se guardara en el servidor, pero no quería guardar los archivos en el servidor, ya que presentaba otros problemas (seguridad: el archivo podía ser accedido por usuarios no autenticados, limpieza: cómo y cuándo se eliminan los archivos). Y como usted, mis datos eran objetos JSON complejos y anidados que serían difíciles de poner en un formulario.
Lo que hice fue crear dos funciones de servidor. El primero validó los datos. Si hubiera un error, sería devuelto. Si no fue un error, devolví todos los parámetros serializados / codificados como una cadena base64. Luego, en el cliente, tengo un formulario que solo tiene una entrada oculta y publicaciones en una segunda función del servidor. Establezco la entrada oculta en la cadena base64 y envío el formato. La segunda función del servidor decodifica / deserializa los parámetros y genera el archivo. El formulario podría enviarse a una nueva ventana o un iframe en la página y el archivo se abrirá.
Hay un poco más de trabajo involucrado, y tal vez un poco más de procesamiento, pero en general, me sentí mucho mejor con esta solución.
El código está en C # / MVC
en el cliente
fuente
Hay una manera más simple, crear un formulario y publicarlo, esto corre el riesgo de restablecer la página si el tipo de mime de retorno es algo que un navegador abriría, pero para csv y tal es perfecto
El ejemplo requiere guión bajo y jquery
Para cosas como html, texto y demás, asegúrese de que el tipo mime sea similar a application / octet-stream
código php
fuente
En resumen, no hay una manera más simple. Debe realizar otra solicitud de servidor para mostrar el archivo PDF. Sin embargo, existen pocas alternativas, pero no son perfectas y no funcionarán en todos los navegadores:
fuente
Content-Disposition: attachment;filename="file.txt"
), pero realmente quiero probar este enfoque la próxima vez. Había usadoURI
datos para miniaturas antes, pero nunca se me ocurrió usarlo para descargas; gracias por compartir esta pepita.Ha pasado un tiempo desde que se hizo esta pregunta, pero tuve el mismo desafío y quiero compartir mi solución. Utiliza elementos de las otras respuestas, pero no pude encontrarlo en su totalidad. No utiliza un formulario o un iframe, pero sí requiere un par de solicitud post / get. En lugar de guardar el archivo entre las solicitudes, guarda los datos de la publicación. Parece ser a la vez simple y efectivo.
cliente
servidor
fuente
fuente
No es una respuesta completa a la publicación original, sino una solución rápida y sucia para publicar un objeto json en el servidor y generar dinámicamente una descarga.
Lado del cliente jQuery:
... y luego decodificando la cadena json en el lado del servidor y configurando los encabezados para descargar (ejemplo de PHP):
fuente
Creo que el mejor enfoque es usar una combinación. Su segundo enfoque parece ser una solución elegante para los navegadores.
Entonces, dependiendo de cómo se realice la llamada. (ya sea un navegador o una llamada de servicio web) puede usar una combinación de ambos, enviando una URL al navegador y enviando datos sin procesar a cualquier otro cliente de servicio web.
fuente
He estado despierto durante dos días tratando de descubrir cómo descargar un archivo usando jquery con llamada ajax. Todo el apoyo que obtuve no pudo ayudar a mi situación hasta que lo intenté.
Lado del cliente
Lado del servidor
Buena suerte.
fuente
Lo encontré en algún lugar hace mucho tiempo y funciona perfectamente.
fuente
Otro enfoque en lugar de guardar el archivo en el servidor y recuperarlo, es usar .NET 4.0+ ObjectCache con una breve caducidad hasta la segunda Acción (momento en el que se puede volcar definitivamente). La razón por la que quiero usar JQuery Ajax para hacer la llamada es porque es asíncrona. Construir mi archivo PDF dinámico lleva bastante tiempo, y visualizo un cuadro de diálogo de spinner ocupado durante ese tiempo (también permite que se realice otro trabajo). El enfoque de usar los datos devueltos en el "éxito:" para crear un Blob no funciona de manera confiable. Depende del contenido del archivo PDF. Se daña fácilmente con los datos en la respuesta, si no es completamente textual, que es todo lo que Ajax puede manejar.
fuente
Solución
El adjunto de disposición de contenido parece funcionar para mí:
Solución alterna
application / octet-stream
Me sucedió algo similar con un JSON; para mí, en el lado del servidor, estaba configurando el encabezado en self.set_header ("Content-Type", " application / json "), sin embargo, cuando lo cambié a:
Lo descargó automáticamente.
También sepa que para que el archivo aún conserve el sufijo .json, lo necesitará en el encabezado del nombre de archivo:
fuente
Con HTML5, puede crear un ancla y hacer clic en él. No es necesario agregarlo al documento de niño.
Todo listo.
Si desea tener un nombre especial para la descarga, simplemente páselo en el
download
atributo:fuente