Scarica il file Excel tramite AJAX MVC


92

Ho un modulo grande (ish) in MVC.

Devo essere in grado di generare un file Excel contenente dati da un sottoinsieme di quel modulo.

La parte complicata è che questo non dovrebbe influenzare il resto del modulo e quindi voglio farlo tramite AJAX. Mi sono imbattuto in alcune domande su SO che sembrano essere correlate, ma non riesco a capire cosa significano le risposte.

Questo sembra il più vicino a quello che sto cercando : asp-net-mvc-download-excel - ma non sono sicuro di aver capito la risposta, e ora ha un paio di anni. Mi sono anche imbattuto in un altro articolo (non lo trovo più) sull'utilizzo di un iframe per gestire il download del file, ma non sono sicuro di come farlo funzionare con MVC.

Il mio file excel ritorna bene se sto facendo un post completo ma non riesco a farlo funzionare con AJAX in mvc.

Risposte:


215

Non è possibile restituire direttamente un file per il download tramite una chiamata AJAX, quindi un approccio alternativo consiste nell'utilizzare una chiamata AJAX per inviare i dati correlati al server. È quindi possibile utilizzare il codice lato server per creare il file Excel (consiglierei di utilizzare EPPlus o NPOI anche se suona come se questa parte funzioni).

AGGIORNAMENTO Settembre 2016

La mia risposta originale (sotto) aveva più di 3 anni, quindi ho pensato di aggiornare poiché non creo più file sul server durante il download di file tramite AJAX, tuttavia, ho lasciato la risposta originale in quanto potrebbe essere di qualche utilità ancora a seconda le vostre esigenze specifiche.

Uno scenario comune nelle mie applicazioni MVC è la creazione di report tramite una pagina Web che ha alcuni parametri di report configurati dall'utente (intervalli di date, filtri, ecc.). Quando l'utente ha specificato i parametri, li inserisce nel server, viene generato il report (ad esempio un file Excel come output) e quindi memorizzo il file risultante come matrice di byte nel TempDatabucket con un riferimento univoco. Questo riferimento viene restituito come risultato Json alla mia funzione AJAX che successivamente reindirizza all'azione separata del controller per estrarre i dati TempDatae scaricarli nel browser degli utenti finali.

Per fornire maggiori dettagli, supponendo che tu abbia una vista MVC che ha un form associato a una classe Model, chiamiamo Model ReportVM.

Innanzitutto, è necessaria un'azione del controller per ricevere il modello pubblicato, un esempio potrebbe essere:

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 chiamata AJAX che invia il mio modulo MVC al controller sopra e riceve la risposta ha questo aspetto:

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

L'azione del controller per gestire il download del file:

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

Un altro cambiamento che potrebbe essere facilmente adattato se necessario è passare il tipo MIME del file come terzo parametro in modo che l'azione del controller possa servire correttamente una varietà di formati di file di output.

Ciò elimina la necessità di creare e archiviare file fisici sul server, quindi non sono necessarie routine di pulizia e, ancora una volta, questo è senza soluzione di continuità per l'utente finale.

Nota, il vantaggio di utilizzare TempDatapiuttosto che Sessionè che una volta TempDataletti i dati vengono cancellati, quindi sarà più efficiente in termini di utilizzo della memoria se si dispone di un volume elevato di richieste di file. Vedere TempData Best Practice .

Risposta ORIGINALE

Non è possibile restituire direttamente un file per il download tramite una chiamata AJAX, quindi un approccio alternativo consiste nell'utilizzare una chiamata AJAX per inviare i dati correlati al server. È quindi possibile utilizzare il codice lato server per creare il file Excel (consiglierei di utilizzare EPPlus o NPOI per questo, anche se sembra che questa parte funzioni).

Una volta che il file è stato creato sul server, restituire il percorso del file (o solo il nome del file) come valore di ritorno alla chiamata AJAX e quindi impostare JavaScript window.locationsu questo URL che richiederà al browser di scaricare il file.

Dal punto di vista degli utenti finali, l'operazione di download dei file è semplice poiché non abbandonano mai la pagina in cui ha origine la richiesta.

Di seguito è riportato un semplice esempio artificioso di una chiamata ajax per ottenere ciò:

$.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;
    }
});
  • Il parametro url è il metodo Controller / Azione in cui il codice creerà il file Excel.
  • Il parametro data contiene i dati JSON che verrebbero estratti dal modulo.
  • returnValue sarebbe il nome file del file Excel appena creato.
  • Il comando window.location reindirizza al metodo Controller / Action che restituisce effettivamente il file per il download.

Un metodo controller di esempio per l'azione Download potrebbe essere:

[HttpGet]
public virtual ActionResult Download(string file)
{   
  string fullPath = Path.Combine(Server.MapPath("~/MyFiles"), file);
  return File(fullPath, "application/vnd.ms-excel", file);
}

3
Sembra una buona opzione potenziale, ma prima di procedere, non ci sono altre alternative che non implichino la creazione prima del file sul server?
Valuk

4
Non che io sappia - questo approccio l'ho usato con successo molte volte. Dal punto di vista degli utenti è senza soluzione di continuità, l'unica cosa da tenere presente è che avrai bisogno di una routine di pulizia per riordinare i file che vengono creati man mano che verranno montati nel tempo.
connectedsoftware

7
La creazione di un endpoint "/ Download? File = ..." SCREAMS un enorme rischio per la sicurezza - Non sono un esperto di sicurezza, ma penso che vorresti aggiungere l'autenticazione dell'utente, l'igiene degli input, MVC [ValidateAntiForgeryToken] e menzionare meglio altri tipi di sicurezza -pratiche per questa risposta.
Jimmy

2
@CSL Ricevo sempre l'errore 0x800a03f6 - Errore di runtime JavaScript: carattere non valido nella risposta var = JSON.parse (dati);
Standage

2
Ottimo, perché non metti la vecchia risposta in fondo? E la nuova risposta in alto, quindi le persone non perdono tempo
goamn

19

I miei 2 centesimi - non è necessario archiviare Excel come file fisico sul server - invece, archiviarlo nella cache (sessione). Usa un nome generato in modo univoco per la tua variabile Cache (che memorizza il file excel): questo sarà il ritorno della tua chiamata (iniziale) ajax. In questo modo non devi affrontare problemi di accesso ai file, gestire (eliminare) i file quando non sono necessari, ecc. E, avendo il file nella cache, è più veloce recuperarlo.


1
Come lo faresti esattamente? Sembra interessante.
Natalia

2
Un esempio sarebbe carino (intendo dire come memorizzarlo nella cache, non generare il file excel).
Tadej

Ma quanto è scalabile? Se un utente sta scaricando diversi rapporti di grandi dimensioni?
Zapnologica

Se sei su Azure, la sessione funzionerà FINO a quando non disattivi ARRAffinity.
JeeShen Lee,

14

Di recente sono stato in grado di farlo in MVC (sebbene non fosse necessario utilizzare AJAX) senza creare un file fisico e ho pensato di condividere il mio codice:

Funzione JavaScript super semplice (il clic sul pulsante datatables.net attiva questo):

function getWinnersExcel(drawingId) {
    window.location = "/drawing/drawingwinnersexcel?drawingid=" + drawingId;
}

Codice controller C #:

    public FileResult DrawingWinnersExcel(int drawingId)
    {
        MemoryStream stream = new MemoryStream(); // cleaned up automatically by MVC
        List<DrawingWinner> winnerList = DrawingDataAccess.GetWinners(drawingId); // simple entity framework-based data retrieval
        ExportHelper.GetWinnersAsExcelMemoryStream(stream, winnerList, drawingId);

        string suggestedFilename = string.Format("Drawing_{0}_Winners.xlsx", drawingId);
        return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml", suggestedFilename);
    }

Nella classe ExportHelper utilizzo uno strumento di terze parti ( GemBox.Spreadsheet ) per generare il file Excel e ha un'opzione Save to Stream. Detto questo, ci sono diversi modi per creare file Excel che possono essere facilmente scritti in un flusso di memoria.

public static class ExportHelper
{
    internal static void GetWinnersAsExcelMemoryStream(MemoryStream stream, List<DrawingWinner> winnerList, int drawingId)
    {

        ExcelFile ef = new ExcelFile();

        // lots of excel worksheet building/formatting code here ...

        ef.SaveXlsx(stream);
        stream.Position = 0; // reset for future read

     }
}

In IE, Chrome e Firefox, il browser richiede di scaricare il file e non si verifica alcuna navigazione effettiva.


8

Prima crea l'azione del controller che creerà il file Excel

[HttpPost]
public JsonResult ExportExcel()
{
    DataTable dt = DataService.GetData();
    var fileName = "Excel_" + DateTime.Now.ToString("yyyyMMddHHmm") + ".xls";

    //save the file to server temp folder
    string fullPath = Path.Combine(Server.MapPath("~/temp"), fileName);

    using (var exportData = new MemoryStream())
    {
        //I don't show the detail how to create the Excel, this is not the point of this article,
        //I just use the NPOI for Excel handler
        Utility.WriteDataTableToExcel(dt, ".xls", exportData);

        FileStream file = new FileStream(fullPath, FileMode.Create, FileAccess.Write);
        exportData.WriteTo(file);
        file.Close();
    }

    var errorMessage = "you can return the errors in here!";

    //return the Excel file name
    return Json(new { fileName = fileName, errorMessage = "" });
}

quindi creare l'azione Download

[HttpGet]
[DeleteFileAttribute] //Action Filter, it will auto delete the file after download, 
                      //I will explain it later
public ActionResult Download(string file)
{
    //get the temp folder and file path in server
    string fullPath = Path.Combine(Server.MapPath("~/temp"), file);

    //return the file for download, this is an Excel 
    //so I set the file content type to "application/vnd.ms-excel"
    return File(fullPath, "application/vnd.ms-excel", file);
}

se vuoi cancellare il file dopo averlo scaricato crealo

public class DeleteFileAttribute : ActionFilterAttribute
{
    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        filterContext.HttpContext.Response.Flush();

        //convert the current filter context to file and get the file path
        string filePath = (filterContext.Result as FilePathResult).FileName;

        //delete the file after download
        System.IO.File.Delete(filePath);
    }
}

e infine ajax call dalla tua vista MVC Razor

//I use blockUI for loading...
$.blockUI({ message: '<h3>Please wait a moment...</h3>' });    
$.ajax({
    type: "POST",
    url: '@Url.Action("ExportExcel","YourController")', //call your controller and action
    contentType: "application/json; charset=utf-8",
    dataType: "json",
}).done(function (data) {
    //console.log(data.result);
    $.unblockUI();

    //get the file name for download
    if (data.fileName != "") {
        //use window.location.href for redirect to download action for download the file
        window.location.href = "@Url.RouteUrl(new 
            { Controller = "YourController", Action = "Download"})/?file=" + data.fileName;
    }
});

7

Ho usato la soluzione pubblicata da CSL ma ti consiglio di non memorizzare i dati del file in Session durante l'intera sessione. Utilizzando TempData i dati del file vengono automaticamente rimossi dopo la richiesta successiva (che è la richiesta GET per il file). È anche possibile gestire la rimozione dei dati del file in Sessione nell'azione di download.

La sessione potrebbe consumare molta memoria / spazio a seconda dell'archiviazione di SessionState e di quanti file vengono esportati durante la sessione e se si hanno molti utenti.

Ho aggiornato il codice lato server da CSL per utilizzare invece TempData.

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

}

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

@Nichlas avevo anche iniziato a usare TempData, la tua risposta mi ha spinto ad aggiornare la mia per riflettere questo!
connectedsoftware

5

using ClosedXML.Excel;

   public ActionResult Downloadexcel()
    {   
        var Emplist = JsonConvert.SerializeObject(dbcontext.Employees.ToList());
        DataTable dt11 = (DataTable)JsonConvert.DeserializeObject(Emplist, (typeof(DataTable)));
        dt11.TableName = "Emptbl";
        FileContentResult robj;
        using (XLWorkbook wb = new XLWorkbook())
        {
            wb.Worksheets.Add(dt11);
            using (MemoryStream stream = new MemoryStream())
            {
                wb.SaveAs(stream);
                var bytesdata = File(stream.ToArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "myFileName.xlsx");
                robj = bytesdata;
            }
        }


        return Json(robj, JsonRequestBehavior.AllowGet);
    }


In AJAX CALL Success Block, successo: function (Rdata) {debugger; var bytes = new Uint8Array (Rdata.FileContents); var blob = new Blob ([bytes], {type: "application / vnd.openxmlformats-officedocument.spreadsheetml.sheet"}); var link = document.createElement ('a'); link.href = window.URL.createObjectURL (blob); link.download = "myFileName.xlsx"; link.click (); },
GVKRAO

qualcuno ha implementato il download del file Excel nel link sopra, funziona solo per @ html.Beginform () quindi dopo piccole modifiche ha bisogno di quel codice, per AJAX chiama Success Block, per favore controllalo, funziona bene in AJAX CALL
GVKRAO

3
$ .ajax ({
                tipo: "GET",
                url: "/ Home / Downloadexcel /",
                contentType: "application / json; charset = utf-8",
                dati: null,
                success: function (Rdata) {
                    debugger;
                    var bytes = new Uint8Array (Rdata.FileContents); 
                    var blob = new Blob ([bytes], {type: "application / vnd.openxmlformats-officedocument.spreadsheetml.sheet"});
                    var link = document.createElement ('a');
                    link.href = window.URL.createObjectURL (blob);
                    link.download = "myFileName.xlsx";
                    link.click ();
                },
                errore: funzione (err) {

                }

            });

1

La risposta accettata non ha funzionato del tutto per me poiché ho ottenuto un risultato 502 Bad Gateway dalla chiamata ajax anche se tutto sembrava tornare bene dal controller.

Forse stavo raggiungendo un limite con TempData - non sono sicuro, ma ho scoperto che se ho usato IMemoryCache invece di TempData , ha funzionato bene, quindi ecco la mia versione adattata del codice nella risposta accettata:

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

        //This is an equivalent to tempdata, but requires manual cleanup
        _cache.Set(handle, memoryStream.ToArray(), 
                    new MemoryCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromMinutes(10))); 
                    //(I'd recommend you revise the expiration specifics to suit your application)

   }      

   // Note we are returning a filename as well as the handle
   return new JsonResult() { 
         Data = new { FileGuid = handle, FileName = "TestReportOutput.xlsx" }
   };

}

La chiamata AJAX rimane come con la risposta accettata (non ho apportato modifiche):

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

L'azione del controller per gestire il download del file:

[HttpGet]
public virtual ActionResult Download(string fileGuid, string fileName)
{   
    if (_cache.Get<byte[]>(fileGuid) != null)
    {
        byte[] data = _cache.Get<byte[]>(fileGuid);
        _cache.Remove(fileGuid); //cleanup here as we don't need it in cache anymore
        return File(data, "application/vnd.ms-excel", fileName);
    }
    else
    {
        // Something has gone wrong...
        return View("Error"); // or whatever/wherever you want to return the user
    }
}

...

Ora c'è del codice extra per configurare MemoryCache ...

Per poter utilizzare "_cache" ho inserito nel costruttore per il controller in questo modo:

using Microsoft.Extensions.Caching.Memory;
namespace MySolution.Project.Controllers
{
 public class MyController : Controller
 {
     private readonly IMemoryCache _cache;

     public LogController(IMemoryCache cache)
     {
        _cache = cache;
     }

     //rest of controller code here
  }
 }

E assicurati di avere quanto segue in ConfigureServices in Startup.cs:

services.AddDistributedMemoryCache();

0

Questo thread mi ha aiutato a creare la mia soluzione che condividerò qui. All'inizio stavo usando una richiesta GET ajax senza problemi, ma è arrivato a un punto in cui la lunghezza dell'URL della richiesta è stata superata, quindi ho dovuto passare a un POST.

Il javascript utilizza il plug-in per il download di file JQuery e consiste in 2 chiamate successive. Un POST (per inviare parametri) e uno GET per recuperare il file.

 function download(result) {
        $.fileDownload(uri + "?guid=" + result,
        {
            successCallback: onSuccess.bind(this),
            failCallback: onFail.bind(this)
        });
    }

    var uri = BASE_EXPORT_METADATA_URL;
    var data = createExportationData.call(this);

    $.ajax({
        url: uri,
        type: 'POST',
        contentType: 'application/json',
        data: JSON.stringify(data),
        success: download.bind(this),
        fail: onFail.bind(this)
    });

Lato server

    [HttpPost]
    public string MassExportDocuments(MassExportDocumentsInput input)
    {
        // Save query for file download use
        var guid = Guid.NewGuid();
        HttpContext.Current.Cache.Insert(guid.ToString(), input, null, DateTime.Now.AddMinutes(5), Cache.NoSlidingExpiration);
        return guid.ToString();
    }

   [HttpGet]
    public async Task<HttpResponseMessage> MassExportDocuments([FromUri] Guid guid)
    {
        //Get params from cache, generate and return
        var model = (MassExportDocumentsInput)HttpContext.Current.Cache[guid.ToString()];
          ..... // Document generation

        // to determine when file is downloaded
        HttpContext.Current
                   .Response
                   .SetCookie(new HttpCookie("fileDownload", "true") { Path = "/" });

        return FileResult(memoryStream, "documents.zip", "application/zip");
    }

0

La risposta di CSL è stata implementata in un progetto su cui sto lavorando, ma il problema che ho riscontrato è stato il ridimensionamento su Azure ha interrotto i nostri download di file. Invece, sono stato in grado di farlo con una chiamata AJAX:

SERVER

[HttpPost]
public FileResult DownloadInvoice(int id1, int id2)
{
    //necessary to get the filename in the success of the ajax callback
    HttpContext.Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");

    byte[] fileBytes = _service.GetInvoice(id1, id2);
    string fileName = "Invoice.xlsx";
    return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
}

CLIENTE (versione modificata del download del file Handle da post ajax )

$("#downloadInvoice").on("click", function() {
    $("#loaderInvoice").removeClass("d-none");

    var xhr = new XMLHttpRequest();
    var params = [];
    xhr.open('POST', "@Html.Raw(Url.Action("DownloadInvoice", "Controller", new { id1 = Model.Id1, id2 = Model.Id2 }))", true);
    xhr.responseType = 'arraybuffer';
    xhr.onload = function () {
        if (this.status === 200) {
            var filename = "";
            var disposition = xhr.getResponseHeader('Content-Disposition');
            if (disposition && disposition.indexOf('attachment') !== -1) {
                var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
                var matches = filenameRegex.exec(disposition);
                if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
            }
            var type = xhr.getResponseHeader('Content-Type');

            var blob = typeof File === 'function'
                ? new File([this.response], filename, { type: type })
                : new Blob([this.response], { type: type });
            if (typeof window.navigator.msSaveBlob !== 'undefined') {
                // IE workaround for "HTML7007: One or more blob URLs were revoked by closing the blob for which they were created. These URLs will no longer resolve as the data backing the URL has been freed."
                window.navigator.msSaveBlob(blob, filename);
            } else {
                var URL = window.URL || window.webkitURL;
                var downloadUrl = URL.createObjectURL(blob);

                if (filename) {
                    // use HTML5 a[download] attribute to specify filename
                    var a = document.createElement("a");
                    // safari doesn't support this yet
                    if (typeof a.download === 'undefined') {
                        window.location = downloadUrl;
                    } else {
                        a.href = downloadUrl;
                        a.download = filename;
                        document.body.appendChild(a);
                        a.click();
                    }
                } else {
                    window.location = downloadUrl;

                }

                setTimeout(function() {
                        URL.revokeObjectURL(downloadUrl);
                    $("#loaderInvoice").addClass("d-none");
                }, 100); // cleanup
            }
        }
    };
    xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    xhr.send($.param(params));
});

0
  $.ajax({
    global: false,
    url: SitePath + "/User/ExportTeamMembersInExcel",
    "data": { 'UserName': UserName, 'RoleId': RoleId, UserIds: AppraseeId },
    "type": "POST",
    "dataType": "JSON",
   "success": function (result) {
        debugger
        var bytes = new Uint8Array(result.FileContents);
        var blob = new Blob([bytes], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
        var link = document.createElement('a');
        link.href = window.URL.createObjectURL(blob);
        link.download = "myFileName.xlsx";
        link.click();
      },
    "error": function () {
        alert("error");
    }
})


[HttpPost]
    public JsonResult ExportTeamMembersInExcel(string UserName, long? RoleId, string[] UserIds)
    {
        MemoryStream stream = new MemoryStream();
        FileContentResult robj;
        DataTable data = objuserservice.ExportTeamToExcel(UserName, RoleId, UserIds);
        using (XLWorkbook wb = new XLWorkbook())
        {
            wb.Worksheets.Add(data, "TeamMembers");
            using (stream)
            {
                wb.SaveAs(stream);
            }
        }
        robj = File(stream.ToArray(), System.Net.Mime.MediaTypeNames.Application.Octet, "TeamMembers.xlsx");
        return Json(robj, JsonRequestBehavior.AllowGet);
    }

non è possibile aprire il file, l'excel si apre e non si chiude da solo, ho persino aggiunto stream.close () appena prima di robj ma non funziona.
dawncode

0

Mi può sembrare abbastanza ingenuo, e può attrarre abbastanza critica, ma ecco come ho fatto,
( Non comporta ajaxper l'esportazione, ma non fa un postback completo sia )

Grazie per questo post e per questa risposta.
Crea un semplice controller

public class HomeController : Controller
{               
   /* A demo action
    public ActionResult Index()
    {           
        return View(model);
    }
   */
    [HttpPost]
    public FileResult ExportData()
    {
        /* An example filter
        var filter = TempData["filterKeys"] as MyFilter;
        TempData.Keep();            */
        var someList = db.GetDataFromDb(/*filter*/) // filter as an example

    /*May be here's the trick, I'm setting my filter in TempData["filterKeys"] 
     in an action,(GetFilteredPartial() illustrated below) when 'searching' for the data,
     so do not really need ajax here..to pass my filters.. */

     //Some utility to convert list to Datatable
     var dt = Utility.ConvertToDataTable(someList); 

      //  I am using EPPlus nuget package 
      using (ExcelPackage pck = new ExcelPackage())
      {
          ExcelWorksheet ws = pck.Workbook.Worksheets.Add("Sheet1");
          ws.Cells["A1"].LoadFromDataTable(dt, true);

            using (var memoryStream = new MemoryStream())
            {                   
              pck.SaveAs(memoryStream);
              return File(memoryStream.ToArray(),
              "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
              "ExportFileName.xlsx");                    
            }                
        }   
    }

    //This is just a supporting example to illustrate setting up filters ..        
   /* [HttpPost]
    public PartialViewResult GetFilteredPartial(MyFilter filter)
    {            
        TempData["filterKeys"] = filter;
        var filteredData = db.GetConcernedData(filter);
        var model = new MainViewModel();
        model.PartialViewModel = filteredData;

        return PartialView("_SomePartialView", model);
    } */     
} 

Ed ecco le viste ..

/*Commenting out the View code, in order to focus on the imp. code     
 @model Models.MainViewModel
 @{Layout...}     

      Some code for, say, a partial View  
      <div id="tblSampleBody">
        @Html.Partial("_SomePartialView", Model.PartialViewModel)
      </div>
  */                                                       
//The actual part.. Just **posting** this bit of data from the complete View...
//Here, you are not posting the full Form..or the complete View
   @using (Html.BeginForm("ExportData", "Home", FormMethod.Post))
    {
        <input type="submit" value="Export Data" />
    }
//...
//</div>

/*And you may require to pass search/filter values.. as said in the accepted answer..
That can be done while 'searching' the data.. and not while
 we need an export..for instance:-             

<script>             
  var filterData = {
      SkipCount: someValue,
      TakeCount: 20,
      UserName: $("#UserName").val(),
      DepartmentId: $("#DepartmentId").val(),     
   }

  function GetFilteredData() {
       $("#loader").show();
       filterData.SkipCount = 0;
       $.ajax({
          url: '@Url.Action("GetFilteredPartial","Home")',
          type: 'POST',
          dataType: "html",
          data: filterData,
          success: function (dataHTML) {
          if ((dataHTML === null) || (dataHTML == "")) {
              $("#tblSampleBody").html('<tr><td>No Data Returned</td></tr>');
                $("#loader").hide();
            } else {
                $("#tblSampleBody").html(dataHTML);                    
                $("#loader").hide();
            }
        }
     });
   }    
</script>*/

L'intero punto del trucco sembra che, stiamo postando un modulo (una parte della vista Razor) su cui stiamo chiamando un Action method, che restituisce: a FileResult, e questo FileResultritorna the Excel File..
E per pubblicare i valori del filtro, come detto, ( e se necessario), sto facendo una richiesta di post a un'altra azione, come è stato tentato di descrivere ..


-1

Sto usando Asp.Net WebForm e voglio solo scaricare un file dal lato server. C'è molto articolo ma non riesco a trovare solo una risposta di base. Ora, ho provato un modo semplice e l'ho ottenuto.

Questo è il mio problema.

Devo creare molti pulsanti di input dinamicamente in fase di esecuzione. E voglio aggiungere ogni pulsante al pulsante di download dando un fileNumber univoco.

Creo ogni pulsante in questo modo:

fragment += "<div><input type=\"button\" value=\"Create Excel\" onclick=\"CreateExcelFile(" + fileNumber + ");\" /></div>";

Ogni pulsante chiama questo metodo ajax.

$.ajax({
    type: 'POST',
    url: 'index.aspx/CreateExcelFile',
    data: jsonData,
    contentType: 'application/json; charset=utf-8',
    dataType: 'json',
    success: function (returnValue) {
      window.location = '/Reports/Downloads/' + returnValue.d;
    }
});

Poi ho scritto un semplice metodo di base.

[WebMethod]
public static string CreateExcelFile2(string fileNumber)
{
    string filePath = string.Format(@"Form_{0}.xlsx", fileNumber);
    return filePath;
}

Sto generando questo Form_1, Form_2, Form_3 .... E cancellerò questi vecchi file con un altro programma. Ma se c'è un modo per inviare semplicemente un array di byte per scaricare il file come usare Response. Voglio usarlo.

Spero che questo possa essere utile per chiunque.


-1

Nel modulo Invia

public ActionResult ExportXls()
{   
 var filePath="";
  CommonHelper.WriteXls(filePath, "Text.xls");
}

 public static void WriteXls(string filePath, string targetFileName)
    {
        if (!String.IsNullOrEmpty(filePath))
        {
            HttpResponse response = HttpContext.Current.Response;
            response.Clear();
            response.Charset = "utf-8";
            response.ContentType = "text/xls";
            response.AddHeader("content-disposition", string.Format("attachment; filename={0}", targetFileName));
            response.BinaryWrite(File.ReadAllBytes(filePath));
            response.End();
        }
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.