Excel dosyasını AJAX MVC ile indirin


94

MVC'de büyük (ish) bir formum var.

Bu formun bir alt kümesinden veri içeren bir excel dosyası oluşturabilmem gerekiyor.

İşin zor yanı, bunun formun geri kalanını etkilememesi ve bu yüzden AJAX aracılığıyla yapmak istiyorum. SO'da birbiriyle ilişkili görünen birkaç soruya rastladım, ancak yanıtların ne anlama geldiğini tam olarak çözemiyorum.

Bu, peşinde olduğum şeye en yakın görünüyor: asp-net-mvc-download-excel - ancak yanıtı anladığımdan emin değilim ve şimdi birkaç yaşında. Dosya indirmeyi işlemek için bir iframe kullanma hakkında başka bir makaleye de rastladım (artık bulamıyorum), ancak bunu MVC ile nasıl çalıştıracağımdan emin değilim.

Tam bir gönderi yapıyorsam excel dosyam iyi dönüyor ancak mvc'de AJAX ile çalışmasını sağlayamıyorum.

Yanıtlar:


218

Bir AJAX çağrısı aracılığıyla bir dosyayı indirmek için doğrudan geri döndüremezsiniz, bu nedenle alternatif bir yaklaşım, ilgili verileri sunucunuza göndermek için bir AJAX çağrısı kullanmaktır. Daha sonra Excel Dosyasını oluşturmak için sunucu tarafı kodunu kullanabilirsiniz (bu bölüm çalışıyormuş gibi görünse de bunun için EPPlus veya NPOI kullanmanızı tavsiye ederim).

GÜNCEL Eylül 2016

Orijinal cevabım (aşağıda) 3 yaşın üzerindeydi, bu yüzden AJAX aracılığıyla dosya indirirken artık sunucuda dosya oluşturmadığım için güncelleme yapacağımı düşündüm, ancak yine de bağlı olarak hala biraz işe yarayabileceği için orijinal cevabı bıraktım. özel gereksinimleriniz.

MVC uygulamalarımdaki yaygın bir senaryo, kullanıcı tarafından yapılandırılmış bazı rapor parametrelerine (Tarih Aralıkları, Filtreler vb.) Sahip bir web sayfası aracılığıyla raporlama yapmaktır. Kullanıcı sunucuya gönderdiği parametreleri belirlediğinde, rapor üretilir (örneğin çıktı olarak bir Excel dosyası) ve ardından elde edilen dosyayı, TempDatabenzersiz bir referansla birlikte bir bayt dizisi olarak depolar . Bu referans, daha sonra verileri TempDatason kullanıcı tarayıcısından çıkarmak ve bu tarayıcıya indirmek için ayrı denetleyici eylemine yönlendiren AJAX işlevime bir Json Sonucu olarak geri aktarılır .

Bunu daha fazla ayrıntı vermek için, Model sınıfına bağlı bir forma sahip bir MVC Görünümünüz olduğunu varsayarak Modeli çağıralım ReportVM.

İlk olarak, yayınlanan modeli almak için bir kontrolör eylemi gereklidir, bir örnek şöyle olabilir:

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

}

MVC formumu yukarıdaki denetleyiciye gönderen ve yanıtı alan AJAX çağrısı şu şekilde görünür:

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

Dosyanın indirilmesini işlemek için denetleyici işlemi:

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

Gerekirse kolayca gerçekleştirilebilecek diğer bir değişiklik, dosyanın MIME Türünü üçüncü bir parametre olarak geçirmektir, böylece bir Denetleyici eylemi çeşitli çıktı dosyası biçimlerine doğru şekilde hizmet edebilir.

Bu, herhangi bir fiziksel dosyanın sunucuda oluşturulması ve depolanması ihtiyacını ortadan kaldırır, böylece temizlik rutinleri gerekmez ve bir kez daha bu son kullanıcı için sorunsuzdur.

Not kullanmanın avantajı TempDataziyade Sessionbir kez olmasıdır TempDatadosya istekleri yüksek hacimli varsa bellek kullanımı açısından daha verimli olacak, böylece veriler temizlenir okunur. TempData En İyi Uygulamasına bakın .

ORİJİNAL Cevap

Bir AJAX çağrısı aracılığıyla bir dosyayı indirmek için doğrudan geri döndüremezsiniz, bu nedenle alternatif bir yaklaşım, ilgili verileri sunucunuza göndermek için bir AJAX çağrısı kullanmaktır. Daha sonra Excel Dosyasını oluşturmak için sunucu tarafı kodunu kullanabilirsiniz (bu bölüm çalışıyormuş gibi görünse de bunun için EPPlus veya NPOI kullanmanızı tavsiye ederim).

Dosya sunucuda oluşturulduktan sonra, AJAX çağrınızın dönüş değeri olarak dosyanın yolunu (veya yalnızca dosya window.locationadını ) geri verin ve ardından JavaScript'i , tarayıcıdan dosyayı indirmesini isteyecek olan bu URL'ye ayarlayın .

Son kullanıcılar açısından bakıldığında, dosya indirme işlemi, talebin kaynaklandığı sayfadan asla ayrılmadıkları için sorunsuzdur.

Aşağıda, bunu başarmak için bir ajax çağrısının basit bir uydurma örneği bulunmaktadır:

$.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;
    }
});
  • url parametresi, kodunuzun Excel dosyasını oluşturacağı Denetleyici / İşlem yöntemidir.
  • data parametresi, formdan çıkarılacak json verilerini içerir.
  • returnValue , yeni oluşturulan Excel dosyanızın dosya adı olacaktır.
  • Window.location aslında indirmek için dosyanızı döndüren Kontrolör / Aksiyon yöntemine komut yönlendirmeler.

İndirme eylemi için örnek bir denetleyici yöntemi şöyle olacaktır:

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

3
Bu iyi bir potansiyel seçenek gibi görünüyor, ancak devam etmeden önce, dosyayı önce sunucuda oluşturmayı içermeyen başka alternatif yok mu?
Valuk

4
Farkında olduğumdan değil - bu yaklaşımı birçok kez başarıyla kullandım. Kullanıcı bakış açısına göre kusursuzdur, dikkat etmeniz gereken tek şey, zaman içinde toplanacakları şekilde oluşturulan dosyaları düzenlemek için bir temizlik rutinine ihtiyacınız olacağıdır.
connectedsoftware

7
Bir uç nokta oluşturma '/ Download? File = ...' SCREAMS büyük güvenlik riski - Ben güvenlik uzmanı değilim, ancak kullanıcı kimlik doğrulaması, giriş temizliği, MVC'ler [ValidateAntiForgeryToken] eklemek ve diğer güvenlikten en iyi şekilde bahsetmek isteyeceğinizi düşünürdüm -bu cevaba yönelik uygulamalar.
Jimmy

2
@CSL Her zaman 0x800a03f6 hatası alıyorum - JavaScript çalışma zamanı hatası: var response = JSON.parse (data) üzerinde geçersiz karakter;
Standage

2
Harika, neden eski cevabı en alta koymuyorsun? Ve en üstteki yeni cevap, böylece insanlar zaman kaybetmesin
goamn

19

Benim 2 sentim - excel'i fiziksel bir dosya olarak sunucuda saklamanıza gerek yok - bunun yerine (Oturum) Önbelleğinde saklayın. Önbellek değişkeniniz için benzersiz olarak oluşturulmuş bir ad kullanın (o excel dosyasını saklar) - bu, (ilk) ajax çağrınızın dönüşü olacaktır. Bu şekilde, dosya erişim sorunları ile uğraşmak zorunda kalmazsınız, dosyaları gerekmediğinde yönetmek (silmek) vb. Ve dosyayı Önbelleğe almak daha hızlıdır.


1
Bunu tam olarak nasıl yaparsınız? Kulağa ilginç geliyor.
Natalia

2
Bir örnek güzel olurdu (excel dosyasını oluşturmak değil, önbellekte nasıl saklayacağımı kastediyorum).
Tadej

Yine de bu ne kadar ölçeklenebilir? Bir kullanıcı birkaç büyük rapor indiriyorsa?
Zapnologica

Azure'daysanız, siz ARRAffinity'yi KAPATMADAN oturum çalışır.
JeeShen Lee

17

Yakın zamanda bunu MVC'de fiziksel bir dosya oluşturmadan (AJAX kullanmaya gerek olmamasına rağmen) başarabildim ve kodumu paylaşacağımı düşündüm:

Süper basit JavaScript işlevi (datatables.net düğmesi tıklaması bunu tetikler):

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

C # Denetleyici kodu:

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

ExportHelper sınıfında , Excel dosyasını oluşturmak için bir 3. parti araç ( GemBox.Spreadsheet ) kullanıyorum ve bir Akışa Kaydet seçeneğine sahip. Bununla birlikte, bir bellek akışına kolayca yazılabilen Excel dosyaları oluşturmanın birkaç yolu vardır.

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

     }
}

IE, Chrome ve Firefox'ta, tarayıcı dosyayı indirmenizi ister ve gerçek bir gezinme gerçekleşmez.


1
Benzer bir yaklaşımım vardı. Sorun şu ki, indirmenin ne zaman bittiğini bilmiyorsunuz, bu yüzden o lanet önyükleyiciyi durdurabilirsiniz :)
Cătălin Rădoi

8

Önce Excel Dosyasını oluşturacak denetleyici eylemini oluşturun

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

ardından İndirme eylemini oluşturun

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

indirdikten sonra dosyayı silmek istiyorsanız bunu oluşturun

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

ve son olarak, MVC Razor görünümünden ajax araması

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

CSL tarafından yayınlanan çözümü kullandım, ancak dosya verilerini tüm oturum boyunca Oturumda saklamamanızı tavsiye ederim. TempData kullanılarak, dosya verileri sonraki istekten sonra otomatik olarak kaldırılır (bu, dosya için GET isteğidir). Ayrıca, indirme eyleminde Oturum'da dosya verilerinin kaldırılmasını da yönetebilirsiniz.

Oturum, SessionState depolamasına ve oturum sırasında kaç dosyanın dışa aktarıldığına ve çok sayıda kullanıcınız olup olmadığına bağlı olarak çok fazla bellek / alan tüketebilir.

Bunun yerine TempData'yı kullanmak için CSL'den sunucu tarafı kodunu güncelledim.

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 Ben de TempData kullanmaya başladım, cevabınız benimkini bunu yansıtacak şekilde güncellememi istedi!
connectedsoftware

5

ClosedXML.Excel kullanarak;

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


AJAX CALL Success Block'ta, başarı: function (Rdata) {debugger; var bytes = new Uint8Array (Rdata.FileContents); var blob = new Blob ([bayt], {tür: "application / vnd.openxmlformats-officedocument.spreadsheetml.sheet"}); var link = document.createElement ('a'); link.href = window.URL.createObjectURL (blob); link.download = "DosyamAd.xlsx"; link.click (); },
GVKRAO

bazıları Excel dosyası indirme işlemini yukarıdaki bağlantıya uyguladı, yalnızca @ html için çalışıyor. Başlarken, küçük değişikliklerden sonra bu koda ihtiyaç duyuyor, AJAX çağrısı Başarı Bloğu için, Lütfen Kontrol Edin, AJAX CALL
GVKRAO'da

3
$ .ajax ({
                type: "GET",
                url: "/ Ana Sayfa / Downloadexcel /",
                contentType: "application / json; charset = utf-8",
                veri: boş,
                başarı: function (Rdata) {
                    hata ayıklayıcı;
                    var bytes = new Uint8Array (Rdata.FileContents); 
                    var blob = new Blob ([bayt], {tür: "application / vnd.openxmlformats-officedocument.spreadsheetml.sheet"});
                    var link = document.createElement ('a');
                    link.href = window.URL.createObjectURL (blob);
                    link.download = "DosyamAd.xlsx";
                    link.click ();
                },
                hata: function (err) {

                }

            });

1

Her şey denetleyiciden geri dönüyormuş gibi görünse de, ajax çağrısından 502 Kötü Ağ Geçidi sonucu aldığım için kabul edilen yanıt benim için pek işe yaramadı .

Belki de TempData ile bir sınıra ulaşıyordum - emin değilim, ancak TempData yerine IMemoryCache kullanırsam iyi çalıştığını gördüm, bu yüzden kabul edilen cevapta kodun uyarlanmış versiyonu:

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

}

AJAX çağrısı, kabul edilen cevapta olduğu gibi kalır (ben değişiklik yapmadım):

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

Dosyanın indirilmesini işlemek için denetleyici işlemi:

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

...

Artık MemoryCache'yi kurmak için bazı ekstra kodlar var ...

"_Cache" kullanmak için yapıcıya denetleyici için şu şekilde enjekte ettim:

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

Ve Startup.cs içindeki ConfigureServices içinde aşağıdakilere sahip olduğunuzdan emin olun:

services.AddDistributedMemoryCache();

0

Bu ileti dizisi, burada paylaşacağım kendi çözümümü oluşturmama yardımcı oldu. İlk başta sorunsuz bir GET ajax isteği kullanıyordum, ancak istek URL uzunluğunun aşıldığı bir noktaya geldi, bu yüzden bir POST'a geçmek zorunda kaldım.

Javascript, JQuery dosya indirme eklentisini kullanır ve birbirini izleyen 2 çağrıdan oluşur. Dosyayı geri almak için bir POST (parametreleri göndermek için) ve bir GET.

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

Sunucu tarafı

    [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

CSL'nin cevabı, üzerinde çalıştığım bir projede uygulandı, ancak karşılaştığım sorun Azure'da ölçeklendirmekti, dosya indirmelerimizi bozdu. Bunun yerine, bunu bir AJAX çağrısıyla yapabildim:

SUNUCU

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

İSTEMCİ ( ajax gönderisinden Tutamaç dosyası indirmenin değiştirilmiş sürümü )

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

dosyayı açamıyor, excel sadece açılıyor ve kendini kapatmıyor, robj'dan hemen önce stream.close () ekledim ama çalışmıyor.
şafak kodu

0

Oldukça naif gelebilir, ve olabilecek oldukça eleştiri çekmek ama burada öyle yapmıştım,
( O içermeyen ajaxihracat için, ama yapmaz ya Tam bir geri gönderme )

Bu gönderi ve bu cevap için teşekkürler .
Basit bir denetleyici oluşturun

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

Ve işte Görünümler ..

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

Bütün mesele hile gibi görünüyor, bir formu (a gönderiyorsunuz kısmını biz bunun üzerine Razor View) çağırarak bir Action methodhangi döner: Bir FileResultve bu FileResultdöner the Excel File..
dedi Ve filtre değerlerini yazabilmek için, ( ve gerekirse), açıklanmaya çalışıldığı gibi başka bir eyleme gönderi isteği gönderiyorum ..


-1

Asp.Net WebForm kullanıyorum ve sadece sunucu tarafından bir dosya indirmek istiyorum. Çok fazla makale var ama sadece temel cevabı bulamıyorum. Şimdi basit bir yol denedim ve anladım.

Bu benim sorunum.

Çalışma zamanında dinamik olarak çok sayıda giriş düğmesi oluşturmam gerekiyor. Ve her düğmeyi benzersiz bir dosya numarası vererek indirme düğmesine eklemek istiyorum.

Her düğmeyi şu şekilde oluşturuyorum:

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

Her düğme bu ajax yöntemini çağırır.

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

Sonra basit ve basit bir yöntem yazdım.

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

Bu Form_1, Form_2, Form_3'ı oluşturuyorum .... Ve bu eski dosyaları başka bir programla sileceğim. Ancak, Response'u kullanmak gibi dosyayı indirmek için sadece bayt dizisi göndermenin bir yolu varsa. Ben kullanmak istiyorum.

Umarım bu herkes için yararlı olur.


-1

Form gönderildiğinde

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();
        }
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.