Télécharger le fichier Excel via AJAX MVC


92

J'ai une grande forme (ish) dans MVC.

J'ai besoin de pouvoir générer un fichier Excel contenant des données d'un sous-ensemble de ce formulaire.

Le plus délicat est que cela ne devrait pas affecter le reste du formulaire et je veux donc le faire via AJAX. J'ai rencontré quelques questions sur SO qui semblent être liées, mais je ne peux pas vraiment comprendre ce que signifient les réponses.

Celui-ci semble le plus proche de ce que je recherche: asp-net-mvc-download-excel - mais je ne suis pas sûr de comprendre la réponse, et il a maintenant quelques années. Je suis également tombé sur un autre article (je ne le trouve plus) sur l'utilisation d'un iframe pour gérer le téléchargement de fichier, mais je ne sais pas comment faire fonctionner cela avec MVC.

Mon fichier Excel retourne bien si je fais un post complet mais je ne peux pas le faire fonctionner avec AJAX dans mvc.

Réponses:


215

Vous ne pouvez pas renvoyer directement un fichier à télécharger via un appel AJAX.Une approche alternative consiste donc à utiliser un appel AJAX pour publier les données associées sur votre serveur. Vous pouvez ensuite utiliser le code côté serveur pour créer le fichier Excel (je recommanderais d'utiliser EPPlus ou NPOI pour cela, même si cela semble que cette partie fonctionne).

MISE À JOUR Septembre 2016

Ma réponse originale (ci-dessous) avait plus de 3 ans, j'ai donc pensé que je mettrais à jour car je ne crée plus de fichiers sur le serveur lors du téléchargement de fichiers via AJAX, cependant, j'ai laissé la réponse originale car elle peut être d'une certaine utilité en fonction de vos besoins spécifiques.

Un scénario courant dans mes applications MVC consiste à générer des rapports via une page Web contenant certains paramètres de rapport configurés par l'utilisateur (plages de dates, filtres, etc.). Lorsque l'utilisateur a spécifié les paramètres qu'il publie sur le serveur, le rapport est généré (par exemple, un fichier Excel en sortie), puis je stocke le fichier résultant sous forme de tableau d'octets dans le TempDataseau avec une référence unique. Cette référence est renvoyée en tant que résultat Json à ma fonction AJAX qui redirige ensuite vers une action de contrôleur distincte pour extraire les données TempDataet les télécharger vers le navigateur de l'utilisateur final.

Pour donner plus de détails, en supposant que vous ayez une vue MVC dont le formulaire est lié à une classe Model, appelons le modèle ReportVM.

Tout d'abord, une action du contrôleur est nécessaire pour recevoir le modèle publié, un exemple serait:

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

}

L'appel AJAX qui publie mon formulaire MVC sur le contrôleur ci-dessus et reçoit la réponse ressemble à ceci:

$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'action du contrôleur pour gérer le téléchargement du fichier:

[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 autre changement qui pourrait facilement être adapté si nécessaire est de passer le type MIME du fichier en tant que troisième paramètre afin que l'action d'un contrôleur puisse correctement servir une variété de formats de fichier de sortie.

Cela supprime tout besoin de créer et de stocker des fichiers physiques sur le serveur, de sorte qu'aucune routine de maintenance n'est requise et encore une fois, cela est transparent pour l'utilisateur final.

Notez que l'avantage d'utiliser TempDataplutôt que Sessionc'est qu'une fois TempDatalues, les données sont effacées, ce qui sera plus efficace en termes d'utilisation de la mémoire si vous avez un volume élevé de demandes de fichiers. Voir les meilleures pratiques TempData .

Réponse ORIGINALE

Vous ne pouvez pas renvoyer directement un fichier à télécharger via un appel AJAX.Une approche alternative consiste donc à utiliser un appel AJAX pour publier les données associées sur votre serveur. Vous pouvez ensuite utiliser le code côté serveur pour créer le fichier Excel (je recommanderais d'utiliser EPPlus ou NPOI pour cela, même si cela semble que cette partie fonctionne).

Une fois que le fichier a été créé sur le serveur, renvoyez le chemin d'accès au fichier (ou simplement le nom de fichier) comme valeur de retour à votre appel AJAX, puis définissez le JavaScript window.locationsur cette URL qui invitera le navigateur à télécharger le fichier.

Du point de vue des utilisateurs finaux, l'opération de téléchargement de fichier est transparente car ils ne quittent jamais la page d'où provient la demande.

Vous trouverez ci-dessous un exemple simple d'appels ajax pour y parvenir:

$.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;
    }
});
  • Le paramètre url est la méthode Controller / Action dans laquelle votre code créera le fichier Excel.
  • Le paramètre data contient les données json qui seraient extraites du formulaire.
  • returnValue serait le nom de fichier de votre fichier Excel nouvellement créé.
  • La commande window.location redirige vers la méthode Controller / Action qui renvoie réellement votre fichier pour téléchargement.

Un exemple de méthode de contrôleur pour l'action de téléchargement serait:

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

3
Cela semble être une bonne option potentielle, mais avant de continuer, n'y a-t-il pas d'autres alternatives qui n'impliquent pas de créer d'abord le fichier sur le serveur?
Valuk

4
Non pas que je sache - cette approche que j'ai utilisée avec succès à plusieurs reprises. Du point de vue des utilisateurs, il est transparent, la seule chose à garder à l'esprit est que vous aurez besoin d'une routine de gestion interne pour ranger les fichiers créés au fur et à mesure qu'ils s'accumulent au fil du temps.
logiciel connecté

7
Création d'un point de terminaison '/ Download? File = ...' SCREAMS un risque de sécurité massif - Je ne suis pas un expert en sécurité, mais je pense que vous voudriez ajouter l'authentification utilisateur, l'assainissement des entrées, MVC's [ValidateAntiForgeryToken] et mentionner d'autres meilleurs sécurité -pratiques à cette réponse.
Jimmy

2
@CSL J'obtiens toujours l'erreur 0x800a03f6 - Erreur d'exécution JavaScript: caractère invalide sur la réponse var = JSON.parse (données);
Standage

2
Super, pourquoi ne mettez-vous pas l'ancienne réponse en bas? Et la nouvelle réponse en haut, pour que les gens ne perdent pas de temps
goamn

19

Mes 2 cents - vous n'avez pas besoin de stocker l'Excel en tant que fichier physique sur le serveur - à la place, stockez-le dans le cache (Session). Utilisez un nom généré de manière unique pour votre variable Cache (qui stocke ce fichier Excel) - ce sera le retour de votre appel ajax (initial). De cette façon, vous n'avez pas à gérer les problèmes d'accès aux fichiers, à gérer (supprimer) les fichiers lorsqu'ils ne sont pas nécessaires, etc. et, avoir le fichier dans le cache, il est plus rapide de le récupérer.


1
Comment feriez-vous cela exactement? Ça a l'air intéressant.
Natalia

2
Un exemple serait bien (je veux dire comment le stocker dans le cache, sans générer le fichier Excel).
Tadej

À quel point est-ce évolutif? Si un utilisateur télécharge plusieurs rapports volumineux?
Zapnologica

Si vous êtes sur Azure, la session fonctionnera JUSQU'À ce que vous désactiviez ARRAffinity.
JeeShen Lee

14

J'ai récemment pu accomplir cela dans MVC (bien qu'il n'y ait pas eu besoin d'utiliser AJAX) sans créer de fichier physique et j'ai pensé partager mon code:

Fonction JavaScript super simple (le clic sur le bouton datatables.net déclenche ceci):

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

Code du contrôleur 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);
    }

Dans la classe ExportHelper, j'utilise un outil tiers ( GemBox.Spreadsheet ) pour générer le fichier Excel et il dispose d'une option Enregistrer dans le flux. Cela étant dit, il existe un certain nombre de façons de créer des fichiers Excel qui peuvent facilement être écrits dans un flux mémoire.

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

     }
}

Dans IE, Chrome et Firefox, le navigateur vous invite à télécharger le fichier et aucune navigation réelle ne se produit.


8

Créez d'abord l'action du contrôleur qui créera le fichier 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 = "" });
}

puis créez l'action Télécharger

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

si vous souhaitez supprimer le fichier après le téléchargement, créez-le

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

et enfin appel ajax de votre vue 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

J'ai utilisé la solution publiée par CSL mais je vous recommande de ne pas stocker les données du fichier dans Session pendant toute la session. En utilisant TempData, les données du fichier sont automatiquement supprimées après la demande suivante (qui est la demande GET pour le fichier). Vous pouvez également gérer la suppression des données de fichier dans Session lors de l'action de téléchargement.

La session peut consommer beaucoup de mémoire / d'espace en fonction du stockage SessionState et du nombre de fichiers exportés pendant la session et si vous avez de nombreux utilisateurs.

J'ai mis à jour le code côté sereur de CSL pour utiliser TempData à la place.

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 J'avais commencé à utiliser TempData aussi, votre réponse m'a incité à mettre à jour la mienne pour refléter cela!
connecté

5

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


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

quelqu'un a implémenté le téléchargement de fichier Excel dans le lien ci-dessus, cela ne fonctionne que pour @ html.Beginform () puis après de petits changements ont besoin de ce code, pour AJAX Call Success Block, veuillez le vérifier, cela fonctionne bien dans AJAX CALL
GVKRAO

3
$ .ajax ({
                tapez: "GET",
                url: "/ Accueil / Downloadexcel /",
                contentType: "application / json; charset = utf-8",
                données: nulles,
                succès: function (Rdata) {
                    débogueur;
                    var bytes = new Uint8Array (Rdata.FileContents); 
                    var blob = new Blob ([octets], {type: "application / vnd.openxmlformats-officedocument.spreadsheetml.sheet"});
                    var lien = document.createElement ('a');
                    link.href = window.URL.createObjectURL (blob);
                    link.download = "myFileName.xlsx";
                    link.click ();
                },
                erreur: fonction (err) {

                }

            });

1

La réponse acceptée n'a pas tout à fait fonctionné pour moi car j'ai obtenu un résultat 502 Bad Gateway de l'appel ajax même si tout semblait bien revenir du contrôleur.

Peut-être que j'atteignais une limite avec TempData - pas sûr, mais j'ai trouvé que si j'utilisais IMemoryCache au lieu de TempData , cela fonctionnait bien, voici donc ma version adaptée du code dans la réponse acceptée:

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

}

L'appel AJAX reste comme avec la réponse acceptée (je n'ai fait aucun changement):

$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'action du contrôleur pour gérer le téléchargement du fichier:

[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
    }
}

...

Il y a maintenant un code supplémentaire pour configurer MemoryCache ...

Afin d'utiliser "_cache", j'ai injecté dans le constructeur pour le contrôleur comme ceci:

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

Et assurez-vous que vous disposez des éléments suivants dans ConfigureServices dans Startup.cs:

services.AddDistributedMemoryCache();

0

Ce fil de discussion m'a aidé à créer ma propre solution que je partagerai ici. J'utilisais une demande GET ajax au début sans problèmes, mais cela est arrivé à un point où la longueur de l'URL de la demande était dépassée, j'ai donc dû passer à un POST.

Le javascript utilise le plugin de téléchargement de fichier JQuery et se compose de 2 appels successifs. Un POST (pour envoyer des paramètres) et un GET pour récupérer le fichier.

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

Du côté serveur

    [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 réponse de CSL a été implémentée dans un projet sur lequel je travaille, mais le problème que j'ai rencontré était la mise à l'échelle sur Azure a interrompu nos téléchargements de fichiers. Au lieu de cela, j'ai pu le faire avec un seul appel AJAX:

SERVEUR

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

CLIENT (version modifiée du téléchargement du fichier Handle à partir du 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);
    }

ne peut pas ouvrir le fichier, Excel vient de s'ouvrir et ne se ferme pas ensuite, j'ai même ajouté stream.close () juste avant robj mais ne fonctionne pas.
dawncode

0

Je peux sembler tout à fait naïf, et peut attirer tout à fait critique, mais voici comment je l' ai fait,
( il ne comporte pas ajaxpour l' exportation, mais il ne fait pas une publication complète soit )

Merci pour ce post et cette réponse.
Créer un contrôleur simple

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

Et voici les vues ..

/*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>*/

Le but de l' astuce semble que, nous publions un formulaire (une partie de la vue Razor) sur lequel nous appelons an Action method, qui retourne: a FileResult, et cela FileResultretourne the Excel File..
Et pour afficher les valeurs de filtre, comme dit, ( et si vous le souhaitez), je fais une demande de publication pour une autre action, comme cela a été tenté de le décrire.


-1

J'utilise Asp.Net WebForm et je veux juste télécharger un fichier côté serveur. Il y a beaucoup d'articles mais je ne trouve pas de réponse simple. Maintenant, j'ai essayé une méthode basique et je l'ai obtenue.

C'est mon problème.

Je dois créer de nombreux boutons d'entrée de manière dynamique au moment de l'exécution. Et je veux ajouter chaque bouton au bouton de téléchargement en donnant un numéro de fichier unique.

Je crée chaque bouton comme ceci:

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

Chaque bouton appelle cette méthode 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;
    }
});

Ensuite, j'ai écrit une méthode simple de base.

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

Je génère ce Form_1, Form_2, Form_3 .... Et je vais supprimer ces anciens fichiers avec un autre programme. Mais s'il existe un moyen d'envoyer simplement un tableau d'octets pour télécharger un fichier, comme en utilisant Response. Je veux l'utiliser.

J'espère que ce sera utile pour n'importe qui.


-1

Sur Soumettre le formulaire

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();
        }
    }
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.