No puede devolver directamente un archivo para su descarga a través de una llamada AJAX, por lo que un enfoque alternativo es usar una llamada AJAX para publicar los datos relacionados en su servidor. Luego puede usar el código del lado del servidor para crear el archivo de Excel (recomendaría usar EPPlus o NPOI para esto, aunque parece que tiene esta parte funcionando).
ACTUALIZACIÓN Septiembre de 2016
Mi respuesta original (a continuación) tenía más de 3 años, así que pensé que actualizaría ya que ya no creo archivos en el servidor al descargar archivos a través de AJAX; sin embargo, he dejado la respuesta original, ya que puede ser de alguna utilidad dependiendo de sus requisitos específicos.
Un escenario común en mis aplicaciones MVC es informar a través de una página web que tiene algunos parámetros de informe configurados por el usuario (rangos de fechas, filtros, etc.). Cuando el usuario ha especificado los parámetros, los publica en el servidor, se genera el informe (por ejemplo, un archivo de Excel como salida) y luego almaceno el archivo resultante como una matriz de bytes en el TempData
depósito con una referencia única. Esta referencia se devuelve como un resultado Json a mi función AJAX que posteriormente redirige a una acción de controlador separada para extraer los datos TempData
y descargarlos en el navegador de los usuarios finales.
Para dar más detalles, asumiendo que tiene una Vista MVC que tiene un formulario vinculado a una clase de Modelo, llamemos al Modelo ReportVM
.
Primero, se requiere una acción del controlador para recibir el modelo publicado, un ejemplo sería:
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" }
};
}
La llamada AJAX que publica mi formulario MVC en el controlador anterior y recibe la respuesta se ve así:
$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;
}
})
La acción del controlador para manejar la descarga del archivo:
[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();
}
}
Otro cambio que podría adaptarse fácilmente si fuera necesario es pasar el tipo MIME del archivo como un tercer parámetro para que la única acción del controlador pueda servir correctamente una variedad de formatos de archivo de salida.
Esto elimina la necesidad de crear y almacenar archivos físicos en el servidor, por lo que no se requieren rutinas de limpieza y, una vez más, esto es perfecto para el usuario final.
Tenga en cuenta que la ventaja de usar en TempData
lugar de Session
es que una vez que TempData
se leen, los datos se borran, por lo que será más eficiente en términos de uso de memoria si tiene un gran volumen de solicitudes de archivos. Consulte las mejores prácticas de TempData .
Respuesta ORIGINAL
No puede devolver directamente un archivo para su descarga a través de una llamada AJAX, por lo que un enfoque alternativo es usar una llamada AJAX para publicar los datos relacionados en su servidor. Luego puede usar el código del lado del servidor para crear el archivo de Excel (recomendaría usar EPPlus o NPOI para esto, aunque parece que tiene esta parte funcionando).
Una vez que se haya creado el archivo en el servidor, devuelva la ruta al archivo (o simplemente el nombre del archivo) como valor de retorno a su llamada AJAX y luego configure el JavaScript window.location
en esta URL que le pedirá al navegador que descargue el archivo.
Desde la perspectiva de los usuarios finales, la operación de descarga de archivos es perfecta, ya que nunca abandonan la página en la que se origina la solicitud.
A continuación se muestra un ejemplo simple inventado de una llamada ajax para lograr esto:
$.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;
}
});
- El parámetro url es el método Controller / Action donde su código creará el archivo Excel.
- El parámetro de datos contiene los datos json que se extraerían del formulario.
- returnValue sería el nombre del archivo de Excel recién creado.
- El comando window.location redirige al método Controller / Action que realmente devuelve su archivo para su descarga.
Un método de controlador de muestra para la acción Descargar sería:
[HttpGet]
public virtual ActionResult Download(string file)
{
string fullPath = Path.Combine(Server.MapPath("~/MyFiles"), file);
return File(fullPath, "application/vnd.ms-excel", file);
}