Baixe um arquivo por jQuery.Ajax

Eu tenho uma ação Struts2 no lado do servidor para download de arquivos.

  text/plain imageStream attachment;filename={fileName} 1024   

No entanto, quando eu chamo a ação usando o jQuery:

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

No Firebug, vejo os dados serem recuperados com o stream binário . Gostaria de saber como abrir a janela de download de arquivos com o qual o usuário pode salvar o arquivo localmente?

Bluish está completamente certo sobre isso, você não pode fazê-lo através do Ajax porque o JavaScript não pode salvar arquivos diretamente no computador de um usuário (por questões de segurança). Infelizmente, apontar o URL da janela principal no seu download de arquivos significa que você tem pouco controle sobre a experiência do usuário quando ocorre um download de arquivo.

Eu criei o jQuery File Download, que permite uma experiência “Ajax like” com downloads de arquivos completos com os retornos de chamadas OnSuccess e OnFailure para proporcionar uma melhor experiência ao usuário. Dê uma olhada no meu post no blog sobre o problema comum que o plugin resolve e algumas maneiras de usá-lo e também uma demonstração do jQuery File Download em ação . Aqui está a fonte

Aqui está uma demonstração de caso de uso simples usando a fonte de plugin com promises. A página de demonstração inclui muitos outros exemplos de ‘melhor UX’ também.

 $.fileDownload('some/file.pdf') .done(function () { alert('File download a success!'); }) .fail(function () { alert('File download failed!'); }); 

Dependendo de quais navegadores você precisa oferecer suporte, você pode usar https://github.com/eligrey/FileSaver.js/, o que permite um controle mais explícito do que o método IFRAME que o download do arquivo jQuery usa.

Ninguém postou esta solução do @ Pekka … então vou postar. Pode ajudar alguém.

Você não pode e não precisa fazer isso através do Ajax. Apenas use

 window.location="download.action?para1=value1...." 

Você pode com HTML5

NB: Os dados do arquivo retornados DEVEM ser codificados em base64 porque você não pode codificar JSON dados binários

Na minha resposta AJAX eu tenho uma estrutura de dados que se parece com isso:

 { result: 'OK', download: { mimetype: string(mimetype in the form 'major/minor'), filename: string(the name of the file to download), data: base64(the binary data as base64 to download) } } 

Isso significa que eu posso fazer o seguinte para salvar um arquivo via AJAX

 var a = document.createElement('a'); if (window.URL && window.Blob && ('download' in a) && window.atob) { // Do it the HTML5 compliant way var blob = base64ToBlob(result.download.data, result.download.mimetype); var url = window.URL.createObjectURL(blob); a.href = url; a.download = result.download.filename; a.click(); window.URL.revokeObjectURL(url); } 

A function base64ToBlob foi tirada daqui e deve ser usada em conformidade com esta function

 function base64ToBlob(base64, mimetype, slicesize) { if (!window.atob || !window.Uint8Array) { // The current browser doesn't have the atob function. Cannot continue return null; } mimetype = mimetype || ''; slicesize = slicesize || 512; var bytechars = atob(base64); var bytearrays = []; for (var offset = 0; offset < bytechars.length; offset += slicesize) { var slice = bytechars.slice(offset, offset + slicesize); var bytenums = new Array(slice.length); for (var i = 0; i < slice.length; i++) { bytenums[i] = slice.charCodeAt(i); } var bytearray = new Uint8Array(bytenums); bytearrays[bytearrays.length] = bytearray; } return new Blob(bytearrays, {type: mimetype}); }; 

Isso é bom se o seu servidor está descarregando arquivos para serem salvos. No entanto, ainda não consegui descobrir como implementar um fallback HTML4

1. Quadro agnóstico: arquivo de download do servlet como anexo

   download   download 

2. Struts2 Framework: arquivo de download de ação como anexo

   download   download 

Seria melhor usar tag apontando com OGNL para uma URL criada com a tag :

   value1  download 

Nos casos acima, você precisa escrever o header Content-Disposition na resposta , especificando que o arquivo precisa ser baixado ( attachment ) e não aberto pelo navegador ( inline ). Você também precisa especificar o tipo de conteúdo e adicionar o nome e o comprimento do arquivo (para ajudar o navegador a desenhar uma barra de progresso realista).

Por exemplo, ao baixar um 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... 

Com o Struts2 (a menos que você esteja usando o Action como um Servlet, um hack para streaming direto , por exemplo), você não precisa escrever nada diretamente na resposta; simplesmente usando o tipo de resultado Stream e configurando-o em struts.xml funcionará: EXEMPLO

  application/zip attachment;filename="${fileName}" ${fileLength}  

3. Estrutura agnóstica (/ estrutura Struts2): arquivo de abertura Servlet (/ Action) dentro do navegador

Se você deseja abrir o arquivo dentro do navegador, em vez de baixá-lo, a disposição de conteúdo deve ser definida como inline , mas o destino não pode ser o local da janela atual; você deve segmentar uma nova janela criada por javascript, um na página ou uma nova janela criada on-the-fly com o “discutido” target = “_ blank”:

   download    download    download  

Eu criei pouca function como solução alternativa (inspirada pelo plugin @JohnCulviner):

 // creates iframe and form in it with hidden field, // then submit form with provided data // url - form url // data - data to form field // input_name - form hidden input name function ajax_download(url, data, input_name) { var $iframe, iframe_doc, iframe_html; if (($iframe = $('#download_iframe')).length === 0) { $iframe = $("" ).appendTo("body"); } iframe_doc = $iframe[0].contentWindow || $iframe[0].contentDocument; if (iframe_doc.document) { iframe_doc = iframe_doc.document; } iframe_html = "
" + "
" + ""; iframe_doc.open(); iframe_doc.write(iframe_html); $(iframe_doc).find('form').submit(); }

Demonstração com evento de clique:

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

Ok, com base no código do ndp, aqui está uma versão melhorada do ajax_download;

 function ajax_download(url, data) { var $iframe, iframe_doc, iframe_html; if (($iframe = $('#download_iframe')).length === 0) { $iframe = $("" ).appendTo("body"); } iframe_doc = $iframe[0].contentWindow || $iframe[0].contentDocument; if (iframe_doc.document) { iframe_doc = iframe_doc.document; } iframe_html = "
" Object.keys(data).forEach(function(key){ iframe_html += ""; }); iframe_html +="
"; iframe_doc.open(); iframe_doc.write(iframe_html); $(iframe_doc).find('form').submit(); }

Use isso assim:

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

Os parâmetros são enviados como post params apropriados como se fossem provenientes de uma input, e não como uma string codificada json, como no exemplo anterior.

CAVEAT: Desconfie do potencial de injeção variável nesses formulários. Pode haver uma maneira mais segura de codificar essas variables. Alternativamente, considere escaping deles.

Eu enfrentei o mesmo problema e resolvi com sucesso. Meu caso de uso é isso.

Poste dados JSON no servidor e receba um arquivo excel. Esse arquivo excel é criado pelo servidor e retornado como resposta ao cliente. Faça o download dessa resposta como um arquivo com nome personalizado no 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)); }); 

O trecho acima é apenas o seguinte

  • Postando uma matriz como JSON para o servidor usando XMLHttpRequest.
  • Depois de buscar o conteúdo como um blob (binário), estamos criando um URL para download e anexando-o ao link “a” invisível e clicando nele. Eu fiz uma solicitação POST aqui. Em vez disso, você pode optar por um simples GET também. Não podemos baixar o arquivo através do Ajax, devemos usar XMLHttpRequest.

Aqui precisamos definir cuidadosamente algumas coisas no lado do servidor. Eu coloquei alguns headers no Python Django HttpResponse. Você precisa configurá-los de acordo se usar outras linguagens de programação.

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

Desde que eu baixei o xls (excel) aqui, ajustei o contentType para acima de um. Você precisa configurá-lo de acordo com o seu tipo de arquivo. Você pode usar essa técnica para baixar qualquer tipo de arquivo.

A maneira simples de fazer o navegador fazer o download de um arquivo é fazer o pedido assim:

  function downloadFile(urlToSend) { var req = new XMLHttpRequest(); req.open("GET", urlToSend, true); req.responseType = "blob"; req.onload = function (event) { var blob = req.response; var fileName = req.getResponseHeader("fileName") //if you have the fileName header available var link=document.createElement('a'); link.href=window.URL.createObjectURL(blob); link.download=fileName; link.click(); }; req.send(); } 

Isso abre o pop-up de download do navegador.

Aqui está o que eu fiz, javascript puro e html. Não testei, mas isso deve funcionar em todos os navegadores.

Função Javascript

 var iframe = document.createElement('iframe'); iframe.id = "IFRAMEID"; iframe.style.display = 'none'; document.body.appendChild(iframe); iframe.src = 'SERVERURL'+'?' + $.param($scope.filtro); iframe.addEventListener("load", function () { console.log("FILE LOAD DONE.. Download should start now"); }); 

Usando apenas componentes que são suportados em todos os navegadores, não há bibliotecas adicionais.

insira a descrição da imagem aquiinsira a descrição da imagem aqui

Aqui está o código do controlador JAVA Spring do lado do servidor.

 @RequestMapping(value = "/rootto/my/xlsx", method = RequestMethod.GET) public void downloadExcelFile(@RequestParam(value = "param1", required = false) String param1, HttpServletRequest request, HttpServletResponse response) throws ParseException { Workbook wb = service.getWorkbook(param1); if (wb != null) { try { String fileName = "myfile_" + sdf.format(new Date()); response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setHeader("Content-disposition", "attachment; filename=\"" + fileName + ".xlsx\""); wb.write(response.getOutputStream()); response.getOutputStream().close(); } catch (IOException e) { e.printStackTrace(); } } } 

Adicionando mais algumas coisas para responder acima para baixar um arquivo

Abaixo está um código de fonte java que gera byte Array

 @RequestMapping(value = "/downloadReport", method = { RequestMethod.POST }) public ResponseEntity 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(bos.toByteArray(), respHeaders, HttpStatus.CREATED); } 

Agora, em código javascript usando FileSaver.js, pode baixar um arquivo com código abaixo

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

O acima irá baixar o arquivo

 function downloadURI(uri, name) { var link = document.createElement("a"); link.download = name; link.href = uri; link.click(); } 

No Rails, eu faço assim:

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

O truque é a parte window.location . O método do controlador se parece com:

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

Ok, então aqui está o código de trabalho ao usar o MVC e você está recebendo o arquivo de um controlador

Vamos dizer que você tem sua matriz de bytes declarar e preencher, a única coisa que você precisa fazer é usar a function File (usando System.Web.Mvc)

 byte[] bytes = .... insert your bytes in the array return File(bytes, System.Net.Mime.MediaTypeNames.Application.Octet, "nameoffile.exe"); 

e depois, no mesmo controlador, adicione essas duas funções

 protected override void OnResultExecuting(ResultExecutingContext context) { CheckAndHandleFileResult(context); base.OnResultExecuting(context); } private const string FILE_DOWNLOAD_COOKIE_NAME = "fileDownload"; ///  /// If the current response is a FileResult (an MVC base class for files) then write a /// cookie to inform jquery.fileDownload that a successful file download has occured ///  ///  private void CheckAndHandleFileResult(ResultExecutingContext context) { if (context.Result is FileResult) //jquery.fileDownload uses this cookie to determine that a file download has completed successfully Response.SetCookie(new HttpCookie(FILE_DOWNLOAD_COOKIE_NAME, "true") { Path = "/" }); else //ensure that the cookie is removed in case someone did a file download without using jquery.fileDownload if (Request.Cookies[FILE_DOWNLOAD_COOKIE_NAME] != null) Response.Cookies[FILE_DOWNLOAD_COOKIE_NAME].Expires = DateTime.Now.AddYears(-1); } 

e, em seguida, você poderá chamar seu controlador para fazer o download e obter o retorno de chamada de “sucesso” ou “falha”

 $.fileDownload(mvcUrl('name of the controller'), { httpMethod: 'POST', successCallback: function (url) { //insert success code }, failCallback: function (html, url) { //insert fail code } }); 

Se você quiser usar o download do arquivo jQuery, observe isso para o IE. Você precisa redefinir a resposta ou não fará o download

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

Sua ação pode implementar o ServletResponseAware para acessar getServletResponse()

Eu encontrei uma correção que, embora não seja realmente usando ajax, permite que você use uma chamada javascript para solicitar o download e, em seguida, obter um retorno de chamada quando o download realmente é iniciado. Eu achei isso útil se o link executa um script do lado do servidor que leva um pouco para compor o arquivo antes de enviá-lo. para que você possa alertá-los de que está processando e, quando finalmente enviar o arquivo, remover essa notificação de processamento. É por isso que eu queria tentar carregar o arquivo via ajax para começar, para que eu pudesse ter um evento quando o arquivo fosse solicitado e outro quando ele realmente fosse iniciado.

o js na primeira página

 function expdone() { document.getElementById('exportdiv').style.display='none'; } function expgo() { document.getElementById('exportdiv').style.display='block'; document.getElementById('exportif').src='test2.php?arguments=data'; } 

o iframe

  

então o outro arquivo:

          

Eu acho que há uma maneira de ler dados usando js então nenhum PHP seria necessário. mas eu não sei disso e o servidor que estou usando suporta php, então isso funciona para mim. pensei em compartilhá-lo no caso de ajudar alguém.

É certo que você não pode fazê-lo através da chamada Ajax.

No entanto, há uma solução alternativa.

Passos :

Se você estiver usando form.submit () para fazer o download do arquivo, o que você pode fazer é:

  1. Crie uma chamada ajax de cliente para servidor e armazene o stream de arquivos dentro da session.
  2. Após o “sucesso” ser retornado do servidor, chame seu form.submit () para apenas transmitir o stream de arquivo armazenado na session.

Isso é útil no caso de você querer decidir se o arquivo precisa ou não ser baixado depois de fazer form.submit (), por exemplo: pode haver um caso em que em form.submit (), ocorre uma exceção no lado do servidor e em vez disso de travamento, talvez seja necessário mostrar uma mensagem personalizada no lado do cliente; nesse caso, essa implementação pode ajudar.