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);
}
Mis 2 centavos: no necesita almacenar el Excel como un archivo físico en el servidor; en su lugar, guárdelo en la caché (Sesión). Use un nombre generado de forma única para su variable de caché (que almacena ese archivo de Excel): este será el retorno de su llamada ajax (inicial). De esta manera, no tiene que lidiar con problemas de acceso a archivos, administrar (eliminar) los archivos cuando no los necesita, etc. y, al tener el archivo en la caché, es más rápido recuperarlo.
fuente
Recientemente pude lograr esto en MVC (aunque no había necesidad de usar AJAX) sin crear un archivo físico y pensé en compartir mi código:
Función JavaScript súper simple (el clic del botón datatables.net lo activa):
Código del controlador C #:
En la clase ExportHelper utilizo una herramienta de terceros ( GemBox.Spreadsheet ) para generar el archivo de Excel y tiene una opción Guardar en Stream. Dicho esto, hay varias formas de crear archivos de Excel que se pueden escribir fácilmente en un flujo de memoria.
En IE, Chrome y Firefox, el navegador solicita descargar el archivo y no se produce una navegación real.
fuente
Primero cree la acción del controlador que creará el archivo de Excel
luego crea la acción Descargar
si desea eliminar el archivo después de descargarlo, cree este
y finalmente llamada ajax desde su vista MVC Razor
fuente
Usé la solución publicada por CSL, pero le recomendaría que no almacene los datos del archivo en Session durante toda la sesión. Al usar TempData, los datos del archivo se eliminan automáticamente después de la siguiente solicitud (que es la solicitud GET del archivo). También puede gestionar la eliminación de los datos del archivo en Sesión en la acción de descarga.
La sesión podría consumir mucha memoria / espacio dependiendo del almacenamiento de SessionState y cuántos archivos se exportan durante la sesión y si tiene muchos usuarios.
Actualicé el código del lado del seridor de CSL para usar TempData en su lugar.
fuente
usando ClosedXML.Excel;
fuente
fuente
La respuesta aceptada no funcionó del todo para mí, ya que obtuve un resultado de 502 Bad Gateway de la llamada ajax a pesar de que todo parecía estar volviendo bien desde el controlador.
Quizás estaba alcanzando un límite con TempData, no estoy seguro, pero descubrí que si usaba IMemoryCache en lugar de TempData , funcionaba bien, así que aquí está mi versión adaptada del código en la respuesta aceptada:
La llamada AJAX permanece como con la respuesta aceptada (no hice cambios):
La acción del controlador para manejar la descarga del archivo:
...
Ahora hay un código adicional para configurar MemoryCache ...
Para usar "_cache", inyecté en el constructor para el controlador así:
Y asegúrese de tener lo siguiente en ConfigureServices en Startup.cs:
fuente
Este hilo me ayudó a crear mi propia solución que compartiré aquí. Al principio, estaba usando una solicitud GET ajax sin problemas, pero llegó a un punto en el que se excedió la longitud de la URL de la solicitud, por lo que tuve que cambiar a una POST.
El javascript utiliza el complemento de descarga de archivos JQuery y consta de 2 llamadas sucesivas. Un POST (Para enviar parámetros) y un GET para recuperar el archivo.
Lado del servidor
fuente
La respuesta de CSL se implementó en un proyecto en el que estoy trabajando, pero el problema en el que incurrí fue escalar en Azure rompió nuestras descargas de archivos. En cambio, pude hacer esto con una llamada AJAX:
SERVIDOR
CLIENTE (versión modificada de la descarga del archivo Handle desde la publicación ajax )
fuente
fuente
Puedo sonar bastante ingenuo y puedo atraer bastantes críticas, pero así es como lo hice
( no implica la
ajax
exportación, pero tampoco hace una devolución de datos completa )Gracias por esta publicación y esta respuesta.
Crea un controlador simple
Y aquí están las Vistas ...
El objetivo de la truco parece que, estamos publicando una forma (una parte de la maquinilla de afeitar Vista) sobre la que estamos pidiendo una
Action method
, que devuelve: unaFileResult
, y estaFileResult
devuelvethe Excel File
..Y para la publicación de los valores de filtro, como se ha dicho, ( y si lo necesita), estoy realizando una solicitud de publicación para otra acción, como se ha intentado describir.
fuente
Estoy usando Asp.Net WebForm y solo quiero descargar un archivo del lado del servidor. Hay muchos artículos, pero no puedo encontrar una respuesta básica. Ahora, probé una forma básica y lo conseguí.
Ese es mi problema.
Tengo que crear muchos botones de entrada dinámicamente en tiempo de ejecución. Y quiero agregar cada botón al botón de descarga dando un número de archivo único.
Creo cada botón así:
Cada botón llama a este método ajax.
Luego escribí un método simple básico.
Estoy generando este Form_1, Form_2, Form_3 .... Y voy a eliminar estos archivos antiguos con otro programa. Pero si hay una manera de simplemente enviar una matriz de bytes para descargar el archivo, como usar Response. Quiero usarlo.
Espero que esto sea útil para cualquiera.
fuente
En formulario de envío
fuente