javascript download file - Descarga un archivo por jQuery.Ajax





8 Answers

Nadie publicó esta solución de @ Pekka ... así que la publicaré. Puede ayudar a alguien.

No puedes y no necesitas hacer esto a través de Ajax. Solo usa

window.location="download.action?para1=value1...."
php jsp servlet

Tengo una acción de Struts2 en el lado del servidor para la descarga de archivos.

<action name="download" class="com.xxx.DownAction">
    <result name="success" type="stream">
        <param name="contentType">text/plain</param>
        <param name="inputName">imageStream</param>
        <param name="contentDisposition">attachment;filename={fileName}</param>
        <param name="bufferSize">1024</param>
    </result>
</action>

Sin embargo cuando llamo a la acción usando el jQuery:

$.post(
  "/download.action",{
    para1:value1,
    para2:value2
    ....
  },function(data){
      console.info(data);
   }
);

en Firebug veo que los datos se recuperan con el flujo binario . Me pregunto cómo abrir la ventana de descarga de archivos con la que el usuario puede guardar el archivo localmente.




1. Framework agnostic: Servlet descargando archivo como adjunto

<!-- with JS -->
<a href="javascript:window.location='downloadServlet?param1=value1'">
    download
</a>

<!-- without JS -->
<a href="downloadServlet?param1=value1" >download</a>

2. Struts2 Framework: Acción descargando archivo como adjunto

<!-- with JS -->
<a href="javascript:window.location='downloadAction.action?param1=value1'">
    download
</a>

<!-- without JS -->
<a href="downloadAction.action?param1=value1" >download</a>

Sería mejor usar <s:a> etiqueta <s:a> apunta con OGNL a una URL creada con <s:url> etiqueta <s:url> :

<!-- without JS, with Struts tags: THE RIGHT WAY -->    
<s:url action="downloadAction.action" var="url">
    <s:param name="param1">value1</s:param>
</s:ulr>
<s:a href="%{url}" >download</s:a>

En los casos anteriores, debe escribir el encabezado de Disposición de contenido en la respuesta , especificando que el archivo debe descargarse ( attachment ) y no debe abrirse con el navegador (en inline ). También debe especificar el Tipo de contenido , y es posible que desee agregar el nombre y la longitud del archivo (para ayudar al navegador a dibujar una barra de progreso realista).

Por ejemplo, al descargar un ZIP:

response.setContentType("application/zip");
response.addHeader("Content-Disposition", 
                   "attachment; filename=\"name of my file.zip\"");
response.setHeader("Content-Length", myFile.length()); // or myByte[].length...

Con Struts2 (a menos que esté utilizando el Action como un Servlet, un hack para la transmisión directa , por ejemplo), no necesita escribir directamente nada en la respuesta; simplemente utilizando el tipo de resultado Stream y configurándolo en struts.xml funcionará: EXAMPLE

<result name="success" type="stream">
   <param name="contentType">application/zip</param>
   <param name="contentDisposition">attachment;filename="${fileName}"</param>
   <param name="contentLength">${fileLength}</param>
</result>

3. Framework agnostic (/ Struts2 framework): Servlet (/ Action) abriendo el archivo dentro del navegador

Si desea abrir el archivo dentro del navegador, en lugar de descargarlo, la disposición de Contenido debe configurarse en línea , pero el destino no puede ser la ubicación de la ventana actual; debe apuntar a una nueva ventana creada por javascript, un <iframe> en la página, o una nueva ventana creada sobre la marcha con el objetivo "discutido" = "_ en blanco":

<!-- From a parent page into an IFrame without javascript -->   
<a href="downloadServlet?param1=value1" target="iFrameName">
    download
</a>

<!-- In a new window without javascript --> 
<a href="downloadServlet?param1=value1" target="_blank">
    download
</a>

<!-- In a new window with javascript -->    
<a href="javascript:window.open('downloadServlet?param1=value1');" >
    download
</a>



Ok, basado en el código de ndpu aquí hay una versión mejorada (creo) de ajax_download;

function ajax_download(url, data) {
    var $iframe,
        iframe_doc,
        iframe_html;

    if (($iframe = $('#download_iframe')).length === 0) {
        $iframe = $("<iframe id='download_iframe'" +
                    " style='display: none' src='about:blank'></iframe>"
                   ).appendTo("body");
    }

    iframe_doc = $iframe[0].contentWindow || $iframe[0].contentDocument;
    if (iframe_doc.document) {
        iframe_doc = iframe_doc.document;
    }

    iframe_html = "<html><head></head><body><form method='POST' action='" +
                  url +"'>" 

    Object.keys(data).forEach(function(key){
        iframe_html += "<input type='hidden' name='"+key+"' value='"+data[key]+"'>";

    });

        iframe_html +="</form></body></html>";

    iframe_doc.open();
    iframe_doc.write(iframe_html);
    $(iframe_doc).find('form').submit();
}

Usa esto de esta manera;

$('#someid').on('click', function() {
    ajax_download('/download.action', {'para1': 1, 'para2': 2});
});

Los parámetros se envían como parámetros de publicación adecuados, como si procedieran de una entrada en lugar de una cadena codificada en json, como se muestra en el ejemplo anterior.

AVISO: tenga cuidado con el potencial de inyección variable en esas formas. Puede haber una forma más segura de codificar esas variables. Alternativamente contempla escapar de ellos.




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. Hice una solicitud POST aquí. En su lugar, puede ir por un simple GET también. No podemos descargar el archivo a través de Ajax, debemos usar XMLHttpRequest.

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.




Agregando algunas cosas más a la respuesta anterior para descargar un archivo

A continuación se muestra un código de primavera de Java que genera byte Array

@RequestMapping(value = "/downloadReport", method = { RequestMethod.POST })
    public ResponseEntity<byte[]> downloadReport(
            @RequestBody final SomeObejct obj, HttpServletResponse response) throws Exception {

        OutputStream out = new ByteArrayOutputStream();
        // write something to output stream
        HttpHeaders respHeaders = new HttpHeaders();
        respHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        respHeaders.add("X-File-Name", name);
        ByteArrayOutputStream bos = (ByteArrayOutputStream) out;
        return new ResponseEntity<byte[]>(bos.toByteArray(), respHeaders, HttpStatus.CREATED);
    }

Ahora en el código javascript que usa FileSaver.js, puede descargar un archivo con el siguiente código

var json=angular.toJson("somejsobject");
var url=apiEndPoint+'some url';
var xhr = new XMLHttpRequest();
//headers('X-File-Name')
xhr.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 201) {
        var res = this.response;
        var fileName=this.getResponseHeader('X-File-Name');
        var data = new Blob([res]);
        saveAs(data, fileName); //this from FileSaver.js
    }
}    
xhr.open('POST', url);
xhr.setRequestHeader('Authorization','Bearer ' + token);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.responseType = 'arraybuffer';
xhr.send(json);

Lo anterior descargará el archivo.




En Rails, lo hago de esta manera:

function download_file(file_id) {
  let url       = '/files/' + file_id + '/download_file';
    $.ajax({
    type: 'GET',
    url: url,
    processData: false,
    success: function (data) {
       window.location = url;
    },
    error: function (xhr) {
     console.log(' Error:  >>>> ' + JSON.stringify(xhr));
    }
   });
 }

El truco es la parte window.location . El método del controlador se ve así:

# GET /files/{:id}/download_file/
def download_file
    send_file(@file.file,
          :disposition => 'attachment',
          :url_based_filename => false)
end



Si desea utilizar la descarga de archivos jQuery, tenga en cuenta esto para IE. Necesita restablecer la respuesta o no se descargará

    //The IE will only work if you reset response
    getServletResponse().reset();
    //The jquery.fileDownload needs a cookie be set
    getServletResponse().setHeader("Set-Cookie", "fileDownload=true; path=/");
    //Do the reset of your action create InputStream and return

Su acción puede implementar ServletResponseAware para acceder a getServletResponse()




Es cierto que no puedes hacerlo a través de la llamada Ajax.

Sin embargo, hay una solución.

Pasos

Si está utilizando form.submit () para descargar el archivo, lo que puede hacer es:

  1. Cree una llamada ajax del cliente al servidor y almacene la secuencia de archivos dentro de la sesión.
  2. Una vez que el servidor devuelva el "éxito", llame a su formulario.submit () para simplemente transmitir la secuencia de archivos almacenada en la sesión.

Esto es útil en caso de que desee decidir si es necesario descargar o no el archivo después de hacer form.submit (), por ejemplo: puede haber un caso en el que en form.submit (), se produce una excepción en el lado del servidor y en su lugar Si falla, es posible que deba mostrar un mensaje personalizado en el lado del cliente, en caso de que esta implementación pueda ayudar.






Related