Você não pode retornar um arquivo diretamente para download por meio de uma chamada AJAX, portanto, uma abordagem alternativa é usar uma chamada AJAX para postar os dados relacionados em seu servidor. Em seguida, você pode usar o código do lado do servidor para criar o arquivo do Excel (eu recomendaria usar EPPlus ou NPOI para isso, embora pareça que você tem essa parte funcionando).
ATUALIZAÇÃO setembro 2016
Minha resposta original (abaixo) tinha mais de 3 anos, então pensei em atualizar, pois não crio mais arquivos no servidor ao baixar arquivos via AJAX, no entanto, deixei a resposta original, pois pode ser útil ainda dependendo de seus requisitos específicos.
Um cenário comum em meus aplicativos MVC é o relatório por meio de uma página da web que tem alguns parâmetros de relatório configurados pelo usuário (intervalos de datas, filtros etc.). Quando o usuário especifica os parâmetros que eles publicam no servidor, o relatório é gerado (digamos, por exemplo, um arquivo Excel como saída) e, em seguida, armazeno o arquivo resultante como uma matriz de bytes no TempData
intervalo com uma referência exclusiva. Essa referência é passada de volta como um Resultado Json para minha função AJAX que, subsequentemente, redireciona para a ação do controlador separado para extrair os dados TempData
e baixar para o navegador do usuário final.
Para dar mais detalhes, supondo que você tenha uma MVC View que tem um formulário vinculado a uma classe Model, vamos chamar o Model ReportVM
.
Primeiro, uma ação do controlador é necessária para receber o modelo postado, um exemplo seria:
public ActionResult PostReportPartial(ReportVM model){
// Validate the Model is correct and contains valid data
// Generate your report output based on the model parameters
// This can be an Excel, PDF, Word file - whatever you need.
// As an example lets assume we've generated an EPPlus ExcelPackage
ExcelPackage workbook = new ExcelPackage();
// Do something to populate your workbook
// Generate a new unique identifier against which the file can be stored
string handle = Guid.NewGuid().ToString();
using(MemoryStream memoryStream = new MemoryStream()){
workbook.SaveAs(memoryStream);
memoryStream.Position = 0;
TempData[handle] = memoryStream.ToArray();
}
// Note we are returning a filename as well as the handle
return new JsonResult() {
Data = new { FileGuid = handle, FileName = "TestReportOutput.xlsx" }
};
}
A chamada AJAX que envia meu formulário MVC para o controlador acima e recebe a resposta é semelhante a esta:
$ajax({
cache: false,
url: '/Report/PostReportPartial',
data: _form.serialize(),
success: function (data){
var response = JSON.parse(data);
window.location = '/Report/Download?fileGuid=' + response.FileGuid
+ '&filename=' + response.FileName;
}
})
A ação do controlador para lidar com o download do arquivo:
[HttpGet]
public virtual ActionResult Download(string fileGuid, string fileName)
{
if(TempData[fileGuid] != null){
byte[] data = TempData[fileGuid] as byte[];
return File(data, "application/vnd.ms-excel", fileName);
}
else{
// Problem - Log the error, generate a blank file,
// redirect to another controller action - whatever fits with your application
return new EmptyResult();
}
}
Uma outra mudança que poderia ser facilmente acomodada, se necessária, é passar o tipo MIME do arquivo como um terceiro parâmetro para que a única ação do controlador possa servir corretamente a uma variedade de formatos de arquivo de saída.
Isso elimina qualquer necessidade de qualquer arquivo físico criado e armazenado no servidor, de forma que nenhuma rotina de manutenção seja necessária e, mais uma vez, isso é perfeito para o usuário final.
Observe que a vantagem de usar em TempData
vez de Session
é que, uma vez TempData
lidos, os dados são apagados, portanto, será mais eficiente em termos de uso de memória se você tiver um alto volume de solicitações de arquivo. Consulte a prática recomendada de TempData .
Resposta ORIGINAL
Você não pode retornar um arquivo diretamente para download por meio de uma chamada AJAX, portanto, uma abordagem alternativa é usar uma chamada AJAX para postar os dados relacionados em seu servidor. Você pode então usar o código do lado do servidor para criar o arquivo do Excel (eu recomendaria usar EPPlus ou NPOI para isso, embora pareça que você tem essa parte funcionando).
Depois que o arquivo tiver sido criado no servidor, devolva o caminho para o arquivo (ou apenas o nome do arquivo) como o valor de retorno para sua chamada AJAX e, em seguida, defina o JavaScript window.location
para este URL que solicitará ao navegador para baixar o arquivo.
Do ponto de vista dos usuários finais, a operação de download do arquivo é contínua, pois eles nunca saem da página de origem da solicitação.
Abaixo está um exemplo planejado simples de uma chamada de ajax para conseguir isso:
$.ajax({
type: 'POST',
url: '/Reports/ExportMyData',
data: '{ "dataprop1": "test", "dataprop2" : "test2" }',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function (returnValue) {
window.location = '/Reports/Download?file=' + returnValue;
}
});
- O parâmetro url é o método Controlador / Ação onde seu código criará o arquivo Excel.
- O parâmetro data contém os dados json que seriam extraídos do formulário.
- returnValue seria o nome do arquivo do seu arquivo Excel recém-criado.
- O comando window.location redireciona para o método Controller / Action que realmente retorna seu arquivo para download.
Um método de controlador de amostra para a ação de download seria:
[HttpGet]
public virtual ActionResult Download(string file)
{
string fullPath = Path.Combine(Server.MapPath("~/MyFiles"), file);
return File(fullPath, "application/vnd.ms-excel", file);
}
Meus 2 centavos - você não precisa armazenar o Excel como um arquivo físico no servidor - em vez disso, armazene-o no Cache (Sessão). Use um nome gerado exclusivamente para sua variável de Cache (que armazena aquele arquivo do Excel) - este será o retorno de sua chamada ajax (inicial). Desta forma, você não precisa lidar com problemas de acesso ao arquivo, gerenciando (deletando) os arquivos quando não são necessários, etc. e, tendo o arquivo no Cache, é mais rápido recuperá-lo.
fonte
Recentemente, consegui fazer isso em MVC (embora não houvesse necessidade de usar AJAX) sem criar um arquivo físico e pensei em compartilhar meu código:
Função JavaScript super simples (o clique do botão datatables.net aciona isso):
Código do controlador C #:
Na classe ExportHelper eu uso uma ferramenta de terceiros ( GemBox.Spreadsheet ) para gerar o arquivo Excel e tem uma opção Salvar no Fluxo. Dito isso, há várias maneiras de criar arquivos Excel que podem ser facilmente gravados em um fluxo de memória.
No IE, Chrome e Firefox, o navegador solicita o download do arquivo e nenhuma navegação real ocorre.
fonte
Primeiro crie a ação do controlador que criará o arquivo Excel
em seguida, crie a ação Download
se você deseja excluir o arquivo após o download, crie este
e, finalmente, ligue para ajax de sua visualização MVC Razor
fonte
Usei a solução postada por CSL, mas recomendo que você não armazene os dados do arquivo na sessão durante toda a sessão. Usando TempData, os dados do arquivo são removidos automaticamente após a próxima solicitação (que é a solicitação GET para o arquivo). Você também pode gerenciar a remoção dos dados do arquivo na sessão em ação de download.
A sessão pode consumir muita memória / espaço dependendo do armazenamento do SessionState e de quantos arquivos são exportados durante a sessão e se você tem muitos usuários.
Eu atualizei o código do lado do seridor de CSL para usar TempData.
fonte
usando ClosedXML.Excel;
fonte
fonte
A resposta aceita não funcionou muito bem para mim, pois obtive um resultado de 502 Bad Gateway da chamada ajax, embora tudo parecesse estar retornando bem do controlador.
Talvez eu estivesse atingindo um limite com TempData - não tenho certeza, mas descobri que se usei IMemoryCache em vez de TempData , funcionou bem, então aqui está minha versão adaptada do código na resposta aceita:
A chamada AJAX permanece como com a resposta aceita (não fiz alterações):
A ação do controlador para lidar com o download do arquivo:
...
Agora, há algum código extra para configurar o MemoryCache ...
Para usar "_cache" injetei no construtor do controlador assim:
E certifique-se de ter o seguinte em ConfigureServices em Startup.cs:
fonte
Este tópico me ajudou a criar minha própria solução que compartilharei aqui. Eu estava usando uma solicitação GET ajax a princípio sem problemas, mas cheguei a um ponto em que o comprimento da URL da solicitação foi excedido, então tive que mudar para um POST.
O javascript usa o plugin de download de arquivo JQuery e consiste em 2 chamadas sucessivas. Um POST (para enviar parâmetros) e um GET para recuperar o arquivo.
Lado do servidor
fonte
A resposta de CSL foi implementada em um projeto no qual estou trabalhando, mas o problema em que incorri foi o dimensionamento no Azure quebrou nossos downloads de arquivos. Em vez disso, consegui fazer isso com uma chamada AJAX:
SERVIDOR
CLIENT (versão modificada do download do arquivo Handle da postagem ajax )
fonte
fonte
I pode soar bastante ingênuo, e pode atrair bastante crítica, mas aqui está como eu fiz isso,
( Ela não envolve
ajax
para exportação, mas não faz um postback completo, quer )Obrigado por este post e esta resposta.
Crie um controlador simples
E aqui estão as visualizações ..
Todo o ponto do truque parece que estamos postando um formulário (uma parte da Visão do Razor) no qual estamos chamando um
Action method
, que retorna: aFileResult
, e esteFileResult
retornathe Excel File
..E para postar os valores do filtro, como disse, ( e se você precisar), estou fazendo uma solicitação de postagem para outra ação, conforme tentamos descrever ..
fonte
Estou usando o Asp.Net WebForm e só quero baixar um arquivo do lado do servidor. Há muito artigo, mas não consigo encontrar apenas uma resposta básica. Agora, tentei uma maneira básica e consegui.
Esse é meu problema.
Eu tenho que criar muitos botões de entrada dinamicamente no tempo de execução. E eu quero adicionar cada botão para botão de download dando um único fileNumber.
Eu crio cada botão assim:
Cada botão chama esse método ajax.
Então escrevi um método básico simples.
Estou gerando este Form_1, Form_2, Form_3 .... E vou deletar esses arquivos antigos com outro programa. Mas se houver uma maneira de apenas enviar matriz de bytes para baixar o arquivo, como usar o Response. Eu quero usar isso.
Espero que seja útil para alguém.
fonte
Ao enviar formulário
fonte