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