Eu tenho um webapp de página única baseado em jquery. Ele se comunica com um serviço da web RESTful por meio de chamadas AJAX.
Estou tentando fazer o seguinte:
- Envie um POST que contenha dados JSON para um URL REST.
- Se a solicitação especificar uma resposta JSON, JSON será retornado.
- Se a solicitação especificar uma resposta PDF / XLS / etc, um binário para download será retornado.
Eu tenho 1 e 2 trabalhando agora, e o aplicativo jquery do cliente exibe os dados retornados na página da web, criando elementos DOM com base nos dados JSON. Eu também tenho o # 3 trabalhando do ponto de vista do serviço da web, o que significa que ele criará e retornará um arquivo binário se tiver os parâmetros JSON corretos. Mas não tenho certeza da melhor maneira de lidar com o número 3 no código javascript do cliente.
É possível recuperar um arquivo para download a partir de uma chamada ajax como esta? Como obtenho o navegador para baixar e salvar o arquivo?
$.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;
}
});
O servidor responde com os seguintes cabeçalhos:
Content-Disposition:attachment; filename=export-1282022272283.pdf
Content-Length:5120
Content-Type:application/pdf
Server:Jetty(6.1.11)
Outra idéia é gerar o PDF e armazená-lo no servidor e retornar JSON que inclui uma URL para o arquivo. Em seguida, emita outra chamada no manipulador de sucesso do ajax para fazer algo como o seguinte:
success: function(json,status) {
window.location.href = json.url;
}
Mas fazer isso significa que eu precisaria fazer mais de uma chamada para o servidor, e meu servidor precisaria criar arquivos para download, armazená-los em algum lugar e, em seguida, limpar periodicamente a área de armazenamento.
Deve haver uma maneira mais simples de conseguir isso. Ideias?
EDIT: Depois de revisar os documentos para $ .ajax, vejo que o dataType de resposta pode ser apenas um deles xml, html, script, json, jsonp, text
, então acho que não há como baixar diretamente um arquivo usando uma solicitação ajax, a menos que eu incorpore o arquivo binário usando Esquema de URI de dados, conforme sugerido na resposta @VinayC (que não é algo que eu quero fazer).
Então, acho que minhas opções são:
Não use ajax e, em vez disso, envie uma postagem de formulário e incorpore meus dados JSON aos valores do formulário. Provavelmente precisaria mexer com iframes ocultos e tal.
Não use ajax e, em vez disso, converta meus dados JSON em uma sequência de consultas para criar uma solicitação GET padrão e defina window.location.href para este URL. Pode ser necessário usar event.preventDefault () no meu manipulador de cliques para impedir que o navegador seja alterado a partir do URL do aplicativo.
Use minha outra idéia acima, mas aprimorada com sugestões da resposta @naikus. Envie a solicitação AJAX com algum parâmetro que permita ao serviço da web saber que isso está sendo chamado por meio de uma chamada ajax. Se o serviço da web for chamado a partir de uma chamada ajax, basta retornar JSON com uma URL para o recurso gerado. Se o recurso for chamado diretamente, retorne o arquivo binário real.
Quanto mais eu penso sobre isso, mais eu gosto da última opção. Dessa forma, posso recuperar informações sobre a solicitação (tempo para gerar, tamanho do arquivo, mensagens de erro etc.) e posso agir com base nessas informações antes de iniciar o download. A desvantagem é o gerenciamento extra de arquivos no servidor.
Existem outras maneiras de conseguir isso? Quaisquer prós / contras para esses métodos dos quais devo estar ciente?
fonte
url = 'http://localhost/file.php?file='+ $('input').val(); window.open(url);
A maneira mais fácil de obter os mesmos resultados. Coloque o cabeçalho no arquivo php. Não há necessidade de enviar qualquer ajax ou obter pedidosRespostas:
A solução da letronje funciona apenas para páginas muito simples.
document.body.innerHTML +=
pega o texto HTML do corpo, anexa o HTML iframe e define o innerHTML da página para essa sequência. Isso eliminará qualquer ligação de evento que sua página tenha, entre outras coisas. Crie um elemento e useappendChild
.Ou usando jQuery
O que isso realmente faz: execute uma postagem no /create_binary_file.php com os dados na variável postData; se essa postagem for concluída com êxito, adicione um novo iframe ao corpo da página. A suposição é que a resposta de /create_binary_file.php incluirá um valor 'url', que é o URL do qual o arquivo PDF / XLS / etc gerado pode ser baixado. A adição de um iframe à página que referencia esse URL resultará no navegador, promovendo o usuário a baixar o arquivo, supondo que o servidor da Web tenha a configuração apropriada do tipo MIME.
fonte
+=
e atualizarei meu aplicativo de acordo. Obrigado!Resource interpreted as Document but transferred with MIME type application/pdf
. Em seguida, também me avisa que o arquivo pode ser perigoso. Se eu visitar o retData.url diretamente, nenhum aviso ou problema.Eu tenho brincado com outra opção que usa blobs. Consegui fazer o download de documentos de texto e baixei PDFs (no entanto, eles estão corrompidos).
Usando a API de blob, você poderá fazer o seguinte:
Este é o IE 10+, Chrome 8+, FF 4+. Vejo https://developer.mozilla.org/en-US/docs/Web/API/URL.createObjectURL
Ele só fará o download do arquivo no Chrome, Firefox e Opera. Isso usa um atributo de download na tag anchor para forçar o navegador a fazer o download.
fonte
click
:window.URL.revokeObjectURL(link.href);
Conheço esse tipo de idade, mas acho que encontrei uma solução mais elegante. Eu tive o mesmo problema. O problema que eu estava tendo com as soluções sugeridas era que todas exigiam que o arquivo fosse salvo no servidor, mas eu não queria salvar os arquivos no servidor, porque isso introduzia outros problemas (segurança: o arquivo poderia ser acessado por usuários não autenticados, limpeza: como e quando você se livra dos arquivos). E, como você, meus dados eram objetos JSON complexos e aninhados que seriam difíceis de colocar em um formulário.
O que eu fiz foi criar duas funções de servidor. O primeiro validou os dados. Se houvesse um erro, ele seria retornado. Se não houve um erro, retornei todos os parâmetros serializados / codificados como uma string base64. Em seguida, no cliente, tenho um formulário que possui apenas uma entrada oculta e envia para uma segunda função de servidor. Defino a entrada oculta para a string base64 e submeto o formato. A segunda função do servidor decodifica / desserializa os parâmetros e gera o arquivo. O formulário pode ser enviado para uma nova janela ou um iframe na página e o arquivo será aberto.
Há um pouco mais de trabalho envolvido, e talvez um pouco mais de processamento, mas, no geral, me senti muito melhor com esta solução.
O código está em C # / MVC
no cliente
fonte
Existe uma maneira mais simples: criar um formulário e publicá-lo; isso corre o risco de redefinir a página se o tipo mime de retorno for algo que um navegador abriria, mas para csv e assim é perfeito
Exemplo requer sublinhado e jquery
Para coisas como html, texto e coisas do tipo, verifique se o tipo de mimet é algo como application / octet-stream
código php
fonte
Em suma, não há maneira mais simples. Você precisa fazer outra solicitação do servidor para mostrar o arquivo PDF. No entanto, existem poucas alternativas, mas elas não são perfeitas e não funcionam em todos os navegadores:
fonte
Content-Disposition: attachment;filename="file.txt"
), mas realmente quero tentar essa abordagem na próxima vez. Eu tinha usadoURI
dados para miniaturas antes, mas nunca me ocorreu usá-los para downloads - obrigado por compartilhar essa pepita.Já faz um tempo desde que essa pergunta foi feita, mas eu tive o mesmo desafio e quero compartilhar minha solução. Ele usa elementos de outras respostas, mas não consegui encontrá-lo na íntegra. Ele não usa um formulário ou iframe, mas requer um par de solicitação de postagem / obtenção. Em vez de salvar o arquivo entre as solicitações, ele salva os dados da postagem. Parece ser simples e eficaz.
cliente
servidor
fonte
fonte
Não é totalmente uma resposta para a postagem original, mas uma solução rápida e suja para postar um objeto json no servidor e gerar dinamicamente um download.
JQuery do lado do cliente:
..e decodificando a string json no servidor e definindo cabeçalhos para download (exemplo PHP):
fonte
Acho que a melhor abordagem é usar uma combinação. Sua segunda abordagem parece ser uma solução elegante em que os navegadores estão envolvidos.
Portanto, dependendo de como a chamada é feita. (seja um navegador ou uma chamada de serviço da web), você pode usar uma combinação dos dois, enviando um URL para o navegador e enviando dados brutos para qualquer outro cliente de serviço da web.
fonte
Estou acordado há dois dias tentando descobrir como baixar um arquivo usando o jquery com chamada ajax. Todo o apoio que recebi não pôde ajudar minha situação até que eu tente isso.
Lado do Cliente
Lado do servidor
Boa sorte.
fonte
Encontrado em algum lugar há muito tempo e funciona perfeitamente!
fonte
Outra abordagem, em vez de salvar o arquivo no servidor e recuperá-lo, é usar o .NET 4.0+ ObjectCache com uma expiração curta até a segunda ação (no momento, ele pode ser descartado definitivamente). O motivo pelo qual desejo usar o JQuery Ajax para fazer a chamada é que é assíncrono. A criação do meu arquivo PDF dinâmico leva bastante tempo e eu exibo uma caixa de diálogo movimentada durante esse período (também permite que outro trabalho seja feito). A abordagem de usar os dados retornados no "success:" para criar um Blob não funciona de maneira confiável. Depende do conteúdo do arquivo PDF. É facilmente corrompido pelos dados na resposta, se não for completamente textual, o que é tudo o que o Ajax pode manipular.
fonte
Solução
O anexo Disposição de conteúdo parece funcionar para mim:
Gambiarra
application / octet-stream
Eu tinha algo semelhante acontecendo comigo com um JSON, para mim, no lado do servidor, eu estava definindo o cabeçalho como self.set_header ("Content-Type", " application / json "), no entanto, quando eu mudei para:
Ele baixou automaticamente.
Saiba também que, para que o arquivo ainda mantenha o sufixo .json, você precisará dele no cabeçalho do nome do arquivo:
fonte
Com o HTML5, você pode apenas criar uma âncora e clicar nela. Não há necessidade de adicioná-lo ao documento quando criança.
Tudo feito.
Se você deseja ter um nome especial para o download, basta passá-lo no
download
atributo:fonte