variable por pasar pagina mostrar mientras link funcion ejemplo ejecutar div data cargar carga asincrona antes javascript jquery ajax

javascript - por - mostrar loading mientras carga pagina jquery



Manejar descarga de archivos desde post ajax (14)

Tengo una aplicación javascript que envía solicitudes POST ajax a una determinada URL. La respuesta puede ser una cadena JSON o un archivo (como un archivo adjunto). Puedo detectar fácilmente el tipo de contenido y la disposición de contenido en mi llamada ajax, pero una vez que detecto que la respuesta contiene un archivo, ¿cómo ofrezco al cliente que lo descargue? He leído una serie de hilos similares aquí, pero ninguno de ellos proporciona la respuesta que estoy buscando.

Por favor, por favor, no publique respuestas que sugieran que no debería usar ajax para esto o que debo redirigir el navegador, porque nada de esto es una opción. Usar un formulario HTML simple tampoco es una opción. Lo que sí necesito es mostrar un diálogo de descarga al cliente. ¿Se puede hacer esto y cómo?

EDITAR:

Aparentemente, esto no se puede hacer, pero hay una solución simple, como lo sugiere la respuesta aceptada. Para cualquier persona que se encuentre con este problema en el futuro, así es como lo resolví:

$.ajax({ type: "POST", url: url, data: params, success: function(response, status, request) { var disp = request.getResponseHeader(''Content-Disposition''); if (disp && disp.search(''attachment'') != -1) { var form = $(''<form method="POST" action="'' + url + ''">''); $.each(params, function(k, v) { form.append($(''<input type="hidden" name="'' + k + ''" value="'' + v + ''">'')); }); $(''body'').append(form); form.submit(); } } });

Básicamente, solo genere un formulario HTML con los mismos parámetros que se usaron en la solicitud AJAX y envíelo.


¿Qué lenguaje del servidor estás usando? En mi aplicación puedo descargar fácilmente un archivo de una llamada AJAX configurando los encabezados correctos en la respuesta de PHP:

Configuración de encabezados del lado del servidor

header("HTTP/1.1 200 OK"); header("Pragma: public"); header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); // The optional second ''replace'' parameter indicates whether the header // should replace a previous similar header, or add a second header of // the same type. By default it will replace, but if you pass in FALSE // as the second argument you can force multiple headers of the same type. header("Cache-Control: private", false); header("Content-type: " . $mimeType); // $strFileName is, of course, the filename of the file being downloaded. // This won''t have to be the same name as the actual file. header("Content-Disposition: attachment; filename=/"{$strFileName}/""); header("Content-Transfer-Encoding: binary"); header("Content-Length: " . mb_strlen($strFile)); // $strFile is a binary representation of the file that is being downloaded. echo $strFile;

De hecho, esto "redirigirá" el navegador a esta página de descarga, pero como dijo @ahren en su comentario, no navegará fuera de la página actual.

Se trata de configurar los encabezados correctos, así que estoy seguro de que encontrará una solución adecuada para el lenguaje del servidor que está usando si no es PHP.

Manejando el lado del cliente de respuesta.

Suponiendo que ya sabe cómo realizar una llamada AJAX, en el lado del cliente, ejecuta una solicitud AJAX al servidor. El servidor genera un enlace desde donde se puede descargar este archivo, por ejemplo, la URL de "reenvío" a la que desea apuntar. Por ejemplo, el servidor responde con:

{ status: 1, // ok // unique one-time download token, not required of course message: ''http://yourwebsite.com/getdownload/ska08912dsa'' }

Al procesar la respuesta, inyecta un iframe en su cuerpo y configura el SRC del iframe a la URL que acaba de recibir de esta manera (utilizando jQuery para facilitar este ejemplo):

$("body").append("<iframe src=''" + data.message + "'' style=''display: none;'' ></iframe>");

Si ha configurado los encabezados correctos como se muestra arriba, el iframe forzará un diálogo de descarga sin navegar por el navegador fuera de la página actual.

Nota

Además extra en relación a su pregunta; Creo que es mejor devolver siempre JSON cuando se solicita material con tecnología AJAX. Una vez que haya recibido la respuesta de JSON, puede decidir del lado del cliente qué hacer con ella. Tal vez, por ejemplo, más adelante desee que el usuario haga clic en un enlace de descarga a la URL en lugar de forzar la descarga directamente, en su configuración actual tendría que actualizar tanto el cliente como el servidor para hacerlo.


Aquí es cómo conseguí este https://.com/a/27563953/2845977 trabajo

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

Respuesta actualizada usando download.js

$.ajax({ url: ''<URL_TO_FILE>'', success: download.bind(true, "<FILENAME_TO_SAVE_WITH_EXTENSION>", "<FILE_MIME_TYPE>") });


Aquí está mi solución utilizando una forma oculta temporal.

//Create an hidden form var form = $(''<form>'', {''method'': ''POST'', ''action'': this.href}).hide(); //Add params var params = { ...your params... }; $.each(params, function (k, v) { form.append($(''<input>'', {''type'': ''hidden'', ''name'': k, ''value'': v})); }); //Make it part of the document and submit $(''body'').append(form); form.submit(); //Clean up form.remove();

Tenga en cuenta que uso JQuery de forma masiva, pero puede hacer lo mismo con JS nativo.


Como han dicho otros, puede crear y enviar un formulario para descargar a través de una solicitud POST. Sin embargo, no tienes que hacer esto manualmente.

Una biblioteca realmente simple para hacer exactamente esto es jquery.redirect . Proporciona una API similar al método estándar jQuery.post :

$.redirect(url, [values, [method, [target]]])


Cree un formulario, use el método POST, envíe el formulario, no es necesario un iframe. Cuando la página del servidor responda a la solicitud, escriba un encabezado de respuesta para el tipo mime del archivo y aparecerá un cuadro de diálogo de descarga: lo he hecho varias veces.

Desea contenido de tipo de aplicación / descarga: solo busque cómo proporcionar una descarga para cualquier idioma que esté utilizando.


Esta es una pregunta de 3 años, pero hoy tuve el mismo problema. Busqué su solución editada, pero creo que puede sacrificar el rendimiento porque tiene que hacer una doble solicitud. Entonces, si alguien necesita otra solución que no implique llamar al servicio dos veces, esta es la forma en que lo hice:

<form id="export-csv-form" method="POST" action="/the/path/to/file"> <input type="hidden" name="anyValueToPassTheServer" value=""> </form>

Este formulario solo se utiliza para llamar al servicio y evitar usar una window.location (). Después de eso, simplemente debe enviar un formulario desde jquery para llamar al servicio y obtener el archivo. Es bastante simple pero de esta manera puedes hacer una descarga usando un POST . Ahora creo que esto podría ser más fácil si el servicio al que llama es un GET , pero ese no es mi caso.


Me enfrenté al mismo problema y lo resolví con éxito. Mi caso de uso es este.

" Publique datos JSON en el servidor y reciba un archivo de Excel. El servidor crea ese archivo de Excel y se devuelve como respuesta al cliente. Descargue esa respuesta como un archivo con un nombre personalizado en el navegador "

$("#my-button").on("click", function(){ // Data to post data = { ids: [1, 2, 3, 4, 5] }; // Use XMLHttpRequest instead of Jquery $ajax xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { var a; if (xhttp.readyState === 4 && xhttp.status === 200) { // Trick for making downloadable link a = document.createElement(''a''); a.href = window.URL.createObjectURL(xhttp.response); // Give filename you wish to download a.download = "test-file.xls"; a.style.display = ''none''; document.body.appendChild(a); a.click(); } }; // Post data to URL which handles post request xhttp.open("POST", excelDownloadUrl); xhttp.setRequestHeader("Content-Type", "application/json"); // You should set responseType as blob for binary responses xhttp.responseType = ''blob''; xhttp.send(JSON.stringify(data)); });

El fragmento de arriba está haciendo lo siguiente.

  • Publicar una matriz como JSON en el servidor utilizando XMLHttpRequest.
  • Después de obtener el contenido como un blob (binario), estamos creando una URL descargable y adjuntándola al enlace "a" invisible, luego hacemos clic en él.

Aquí tenemos que establecer cuidadosamente algunas cosas en el lado del servidor. Puse algunos encabezados en Python Django HttpResponse. Debe configurarlos en consecuencia si utiliza otros lenguajes de programación.

# In python django code response = HttpResponse(file_content, content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")

Desde que descargué xls (excel) aquí, ajusté contentType a uno superior. Necesita configurarlo de acuerdo a su tipo de archivo. Puedes usar esta técnica para descargar cualquier tipo de archivos.


No se rinda tan rápido, porque esto se puede hacer (en los navegadores modernos) usando partes de FileAPI:

Editar 2017-09-28: actualizado para usar el constructor de archivos cuando esté disponible, así que funciona en Safari> = 10.1.

Edite el 2015-10-16: jQuery ajax no puede manejar las respuestas binarias correctamente (no se puede establecer el Tipo de respuesta), por lo que es mejor usar una llamada de XMLHttpRequest simple.

var xhr = new XMLHttpRequest(); xhr.open(''POST'', url, true); xhr.responseType = ''arraybuffer''; xhr.onload = function () { if (this.status === 200) { 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 = typeof File === ''function'' ? new File([this.response], filename, { type: type }) : new Blob([this.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 } } }; xhr.setRequestHeader(''Content-type'', ''application/x-www-form-urlencoded''); xhr.send($.param(params));

Aquí está la versión antigua usando jQuery.ajax. Podría alterar los datos binarios cuando la respuesta se convierte en una cadena de algún conjunto de caracteres.

$.ajax({ type: "POST", url: url, data: params, success: function(response, status, xhr) { // check for a filename 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 } } });


Para aquellos que buscan una solución desde una perspectiva Angular, esto funcionó para mí:

$http.post( ''url'', {}, {responseType: ''arraybuffer''} ).then(function (response) { var headers = response.headers(); var blob = new Blob([response.data],{type:headers[''content-type'']}); var link = document.createElement(''a''); link.href = window.URL.createObjectURL(blob); link.download = "Filename"; link.click(); });


Para obtener la answer para trabajar en Edge realicé los siguientes cambios:

var blob = typeof File === ''function'' ? new File([this.response], filename, { type: type }) : new Blob([this.response], { type: type });

a esto

var f = typeof File+""; var blob = f === ''function'' && Modernizr.fileapi ? new File([this.response], filename, { type: type }) : new Blob([this.response], { type: type });

Preferiría haber publicado esto como un comentario, pero no tengo suficiente reputación para eso


Quiero señalar algunas dificultades que surgen cuando se utiliza la técnica en la respuesta aceptada, es decir, mediante un formulario de publicación:

  1. No se pueden establecer encabezados en la solicitud. Si su esquema de autenticación involucra encabezados, un Json-Web-Token pasado en el encabezado de Autorización, tendrá que encontrar otra forma de enviarlo, por ejemplo, como un parámetro de consulta.

  2. Realmente no se puede decir cuando la solicitud ha terminado. Bueno, puedes usar una cookie que se establece en respuesta, como lo hace jquery.fileDownload , pero está lejos de ser perfecto. No funcionará para solicitudes concurrentes y se interrumpirá si nunca llega una respuesta.

  3. Si el servidor responde con un error, el usuario será redirigido a la página de errores.

  4. Solo puede utilizar los tipos de contenido admitidos por un form . Lo que significa que no puedes usar JSON.

Terminé usando el método de guardar el archivo en S3 y enviar una URL previamente firmada para obtener el archivo.


Utilicé este FileSaver.js . En mi caso con archivos csv, hice esto (en coffescript):

$.ajax url: "url-to-server" data: "data-to-send" success: (csvData)-> blob = new Blob([csvData], { type: ''text/csv'' }) saveAs(blob, "filename.csv")

Creo que para el caso más complicado, los datos deben ser procesados ​​adecuadamente. Bajo el capó, FileSaver.js implementa el mismo enfoque de la respuesta de .


Veo que ya has encontrado una solución, sin embargo, solo quería agregar algo de información que pueda ayudar a alguien que intenta lograr lo mismo con las grandes solicitudes POST.

Tuve el mismo problema hace un par de semanas, de hecho, no es posible lograr una descarga "limpia" a través de AJAX, el Grupo Filamento creó un complemento jQuery que funciona exactamente como ya lo descubrió, se llama archivo jQuery Descargar sin embargo, hay una desventaja de esta técnica.

Si está enviando grandes solicitudes a través de AJAX (por ejemplo, archivos + 1MB), tendrá un impacto negativo en la capacidad de respuesta. En conexiones de Internet lentas, tendrá que esperar mucho hasta que se envíe la solicitud y también esperar a que se descargue el archivo. No es como un "clic" => "emergente" => "inicio de descarga" instantáneo. Es más como "clic" => "esperar hasta que se envíen los datos" => "esperar respuesta" => "inicio de descarga", lo que hace que parezca que el archivo duplica su tamaño porque tendrá que esperar a que se envíe la solicitud a través de AJAX y recuperarlo como un archivo descargable.

Si está trabajando con archivos de tamaño pequeño <1MB, no lo notará. Pero como descubrí en mi propia aplicación, para archivos de mayor tamaño es casi insoportable.

Mi aplicación permite a los usuarios exportar imágenes generadas dinámicamente, estas imágenes se envían a través de solicitudes POST en formato base64 al servidor (es la única forma posible), luego se procesan y se envían a los usuarios en forma de archivos .png, .jpg, base64 las cadenas para imágenes + 1MB son enormes, esto obliga a los usuarios a esperar más de lo necesario para que el archivo comience a descargarse. En conexiones lentas de internet puede ser realmente molesto.

Mi solución para esto fue escribir temporalmente el archivo en el servidor, una vez que esté listo, generar dinámicamente un enlace al archivo en forma de un botón que cambia entre los estados "Por favor, espere ..." y "Descargar" y al mismo tiempo una vez, imprima la imagen base64 en una ventana emergente de vista previa para que los usuarios puedan "hacer clic derecho" y guardarla. Esto hace que todo el tiempo de espera sea más llevadero para los usuarios y también acelera las cosas.

Actualización 30 de septiembre 2014:

Han pasado meses desde que publiqué esto, finalmente he encontrado un mejor enfoque para acelerar las cosas cuando se trabaja con grandes cadenas de base64. Ahora almaceno cadenas base64 en la base de datos (usando campos de texto largo o largo), luego paso su ID de registro a través de la descarga de archivos jQuery, finalmente, en el archivo de script de descarga consulto la base de datos utilizando este ID para extraer la cadena base64 y pasarla. La función de descarga.

Descargar ejemplo de script:

<?php // Record ID $downloadID = (int)$_POST[''id'']; // Query Data (this example uses CodeIgniter) $data = $CI->MyQueries->GetDownload( $downloadID ); // base64 tags are replaced by [removed], so we strip them out $base64 = base64_decode( preg_replace(''#/[removed/]#'', '''', $data[0]->image) ); // This example is for base64 images $imgsize = getimagesize( $base64 ); // Set content headers header(''Content-Disposition: attachment; filename="my-file.png"''); header(''Content-type: ''.$imgsize[''mime'']); // Force download echo $base64; ?>

Sé que esto va más allá de lo que pidió el OP, sin embargo, sentí que sería bueno actualizar mi respuesta con mis conclusiones. Cuando estaba buscando soluciones a mi problema, leí muchos subprocesos de "Descarga de datos POST de AJAX" que no me dieron la respuesta que estaba buscando, espero que esta información ayude a alguien que busca lograr algo como esto.