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 TempDataintervalo 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 TempDatae 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 TempDatavez de Sessioné que, uma vez TempDatalidos, 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.locationpara 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);
}