Unduh file Excel melalui AJAX MVC

93

Saya memiliki bentuk (ish) besar di MVC.

Saya harus dapat membuat file excel yang berisi data dari bagian formulir itu.

Sedikit rumit adalah bahwa ini seharusnya tidak mempengaruhi sisa formulir dan jadi saya ingin melakukannya melalui AJAX. Saya telah menemukan beberapa pertanyaan tentang SO yang tampaknya terkait, tetapi saya tidak bisa memahami apa artinya jawabannya.

Yang ini sepertinya paling dekat dengan apa yang saya cari: asp-net-mvc-download-excel - tapi saya tidak yakin saya mengerti tanggapannya, dan sekarang sudah berumur beberapa tahun. Saya juga menemukan artikel lain (tidak dapat menemukannya lagi) tentang menggunakan iframe untuk menangani pengunduhan file, tetapi saya tidak yakin bagaimana cara membuatnya berfungsi dengan MVC.

File excel saya kembali dengan baik jika saya melakukan posting lengkap kembali tetapi saya tidak bisa membuatnya bekerja dengan AJAX di mvc.

Valuk
sumber

Jawaban:

218

Anda tidak dapat langsung mengembalikan file untuk diunduh melalui panggilan AJAX, jadi pendekatan alternatifnya adalah menggunakan panggilan AJAX untuk memposting data terkait ke server Anda. Anda kemudian dapat menggunakan kode sisi server untuk membuat File Excel (Saya akan merekomendasikan menggunakan EPPlus atau NPOI untuk ini meskipun kedengarannya seolah-olah Anda memiliki bagian ini berfungsi).

UPDATE September 2016

Jawaban asli saya (di bawah) berusia lebih dari 3 tahun, jadi saya pikir saya akan memperbarui karena saya tidak lagi membuat file di server saat mengunduh file melalui AJAX namun, saya telah meninggalkan jawaban asli karena mungkin ada gunanya masih tergantung pada kebutuhan spesifik Anda.

Skenario umum dalam aplikasi MVC saya melaporkan melalui halaman web yang memiliki beberapa parameter laporan yang dikonfigurasi pengguna (Rentang Tanggal, Filter, dll.). Ketika pengguna telah menentukan parameter yang mereka posting ke server, laporan dibuat (katakanlah misalnya file Excel sebagai output) dan kemudian saya menyimpan file yang dihasilkan sebagai array byte dalam TempDatakeranjang dengan referensi unik. Referensi ini dikirimkan kembali sebagai Hasil Json ke fungsi AJAX saya yang kemudian dialihkan ke tindakan pengontrol terpisah untuk mengekstrak data dari TempDatadan mengunduh ke browser pengguna akhir.

Untuk memberikan detail lebih lanjut, dengan asumsi Anda memiliki Tampilan MVC yang memiliki formulir yang terikat ke kelas Model, mari panggil Model ReportVM.

Pertama, tindakan pengontrol diperlukan untuk menerima model yang diposting, contohnya adalah:

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

}

Panggilan AJAX yang memposting formulir MVC saya ke pengontrol di atas dan menerima respons terlihat seperti ini:

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

Tindakan pengontrol untuk menangani pengunduhan 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();
   }
}

Satu perubahan lain yang dapat dengan mudah diakomodasi jika diperlukan adalah meneruskan Jenis MIME file sebagai parameter ketiga sehingga satu tindakan Pengontrol dapat menyajikan berbagai format file keluaran dengan benar.

Ini menghilangkan semua kebutuhan file fisik untuk dibuat dan disimpan di server, jadi tidak ada rutinitas rumah tangga yang diperlukan dan sekali lagi ini mulus bagi pengguna akhir.

Perhatikan, keuntungan menggunakan TempDatadaripada Sessionsekali TempDatadibaca, data dihapus sehingga akan lebih efisien dalam hal penggunaan memori jika Anda memiliki volume permintaan file yang tinggi. Lihat Praktik Terbaik TempData .

Jawaban ASLI

Anda tidak dapat langsung mengembalikan file untuk diunduh melalui panggilan AJAX, jadi pendekatan alternatifnya adalah menggunakan panggilan AJAX untuk memposting data terkait ke server Anda. Anda kemudian dapat menggunakan kode sisi server untuk membuat File Excel (Saya akan merekomendasikan menggunakan EPPlus atau NPOI untuk ini meskipun kedengarannya seolah-olah Anda memiliki bagian ini berfungsi).

Setelah file dibuat di server, kembalikan path ke file (atau hanya nama file) sebagai nilai kembali ke panggilan AJAX Anda dan kemudian atur JavaScript window.locationke URL ini yang akan meminta browser untuk mendownload file.

Dari perspektif pengguna akhir, operasi pengunduhan file berjalan mulus karena mereka tidak pernah meninggalkan halaman tempat permintaan berasal.

Di bawah ini adalah contoh panggilan ajax yang dibuat sederhana untuk mencapai ini:

$.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;
    }
});
  • Parameter url adalah metode Pengontrol / Tindakan di mana kode Anda akan membuat file Excel.
  • parameter data berisi data json yang akan diekstrak dari formulir.
  • returnValue akan menjadi nama file dari file Excel yang baru Anda buat.
  • The window.location perintah pengalihan dengan metode Controller / Action yang benar-benar mengembalikan file Anda untuk di-download.

Metode pengontrol sampel untuk tindakan Download adalah:

[HttpGet]
public virtual ActionResult Download(string file)
{   
  string fullPath = Path.Combine(Server.MapPath("~/MyFiles"), file);
  return File(fullPath, "application/vnd.ms-excel", file);
}
perangkat lunak yang terhubung
sumber
3
Ini terlihat seperti opsi potensial yang bagus, tetapi sebelum saya melanjutkan, apakah tidak ada alternatif lain yang tidak melibatkan pembuatan file di server terlebih dahulu?
Valuk
4
Bukannya saya sadari - pendekatan ini telah saya gunakan dengan sukses berkali-kali. Dari perspektif pengguna, ini mulus, satu-satunya hal yang perlu diperhatikan adalah Anda akan memerlukan rutinitas tata graha untuk merapikan file yang dibuat karena akan meningkat seiring waktu.
terhubung
7
Membuat titik akhir '/ Download? File = ...' SCREAMS risiko keamanan besar - Saya bukan pakar keamanan, tetapi saya rasa Anda ingin menambahkan otentikasi pengguna, sanitasi input, [ValidateAntiForgeryToken] MVC dan menyebutkan keamanan terbaik lainnya -praktek untuk jawaban ini.
Jimmy
2
@ CSL Saya selalu mendapatkan kesalahan 0x800a03f6 - JavaScript runtime error: Karakter tidak valid pada var response = JSON.parse (data);
Standage
2
Bagus, mengapa Anda tidak meletakkan jawaban lama di bagian bawah? Dan jawaban baru di atas, jadi orang tidak membuang waktu
sial
19

2 sen saya - Anda tidak perlu menyimpan excel sebagai file fisik di server - sebagai gantinya, simpan di Cache (Sesi). Gunakan nama yang dibuat secara unik untuk variabel Cache Anda (yang menyimpan file excel itu) - ini akan menjadi kembalinya panggilan ajax (awal) Anda. Dengan cara ini Anda tidak perlu berurusan dengan masalah akses file, mengelola (menghapus) file saat tidak diperlukan, dll. Dan, memiliki file di Cache, lebih cepat untuk mengambilnya.

Luchian
sumber
1
Bagaimana tepatnya Anda melakukannya? Kedengarannya menarik.
Natalia
2
Sebuah contoh akan bagus (maksud saya bagaimana menyimpannya di cache, bukan menghasilkan file excel).
Tadej
Seberapa skalabel ini? Jika pengguna mengunduh beberapa laporan besar?
Zapnologica
Jika Anda menggunakan Azure, sesi akan bekerja SAMPAI Anda mematikan ARRAffinity.
JeeShen Lee
16

Saya baru-baru ini dapat mencapai ini di MVC (meskipun tidak perlu menggunakan AJAX) tanpa membuat file fisik dan berpikir saya akan membagikan kode saya:

Fungsi JavaScript super sederhana (klik tombol datatables.net memicu ini):

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

C # Kode pengontrol:

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

Di kelas ExportHelper saya menggunakan alat pihak ke-3 ( GemBox.Spreadsheet ) untuk menghasilkan file Excel dan memiliki opsi Save to Stream. Karena itu, ada sejumlah cara untuk membuat file Excel yang dapat dengan mudah ditulis ke aliran memori.

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

     }
}

Di IE, Chrome, dan Firefox, browser meminta untuk mengunduh file dan tidak ada navigasi yang sebenarnya.

Andy S
sumber
Saya memiliki pendekatan serupa. Masalahnya adalah Anda tidak tahu kapan unduhan selesai sehingga Anda dapat menghentikan
prapemuat
8

Pertama Buat tindakan pengontrol yang akan membuat 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 = "" });
}

lalu buat tindakan Unduh

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

jika Anda ingin menghapus file setelah diunduh buat ini

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

dan akhirnya panggilan ajax dari tampilan MVC Razor Anda

//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;
    }
});
Elvin Acevedo
sumber
7

Saya menggunakan solusi yang diposting oleh CSL tetapi saya akan merekomendasikan Anda untuk tidak menyimpan data file dalam Sesi selama seluruh sesi. Dengan menggunakan TempData data file secara otomatis dihapus setelah permintaan berikutnya (yang merupakan permintaan GET untuk file tersebut). Anda juga dapat mengelola penghapusan data file di Sesi dalam tindakan unduhan.

Sesi dapat menghabiskan banyak memori / ruang tergantung pada penyimpanan SessionState dan berapa banyak file yang diekspor selama sesi dan jika Anda memiliki banyak pengguna.

Saya telah memperbarui kode sisi serer dari CSL untuk menggunakan TempData sebagai gantinya.

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();
   }
}
Niclas
sumber
@Nichlas Saya juga mulai menggunakan TempData, jawaban Anda mendorong saya untuk memperbarui milik saya untuk mencerminkan ini!
terhubung
5

menggunakan 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);
    }
GVKRAO
sumber
Dalam AJAX CALL Success Block, sukses: function (Rdata) {debugger; var byte = new Uint8Array (Rdata.FileContents); var blob = blob baru ([byte], {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
seseorang telah menerapkan unduhan file Excel di tautan di atas, ini hanya berfungsi untuk @ html.Beginform () kemudian setelah perubahan kecil perlu kode itu, untuk panggilan AJAX Blok Sukses, Silakan Periksa, berfungsi dengan baik di AJAX CALL
GVKRAO
3
$ .ajax ({
                ketik: "GET",
                url: "/ Beranda / Downloadexcel /",
                contentType: "application / json; charset = utf-8",
                data: null,
                sukses: fungsi (Rdata) {
                    debugger;
                    var byte = new Uint8Array (Rdata.FileContents); 
                    var blob = blob baru ([byte], {type: "application / vnd.openxmlformats-officedocument.spreadsheetml.sheet"});
                    var link = document.createElement ('a');
                    link.href = window.URL.createObjectURL (blob);
                    link.download = "myFileName.xlsx";
                    link.click ();
                },
                kesalahan: fungsi (err) {

                }

            });
GVKRAO
sumber
1

Jawaban yang diterima tidak cukup berhasil untuk saya karena saya mendapat hasil 502 Bad Gateway dari panggilan ajax meskipun semuanya tampak kembali baik-baik saja dari pengontrol.

Mungkin saya mencapai batas dengan TempData - tidak yakin, tetapi saya menemukan bahwa jika saya menggunakan IMemoryCache alih-alih TempData , itu berfungsi dengan baik, jadi inilah versi kode saya yang diadaptasi dalam jawaban yang diterima:

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

}

Panggilan AJAX tetap seperti jawaban yang diterima (saya tidak membuat perubahan):

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

Tindakan pengontrol untuk menangani pengunduhan 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
    }
}

...

Sekarang ada beberapa kode tambahan untuk menyiapkan MemoryCache ...

Untuk menggunakan "_cache", saya memasukkan konstruktor untuk pengontrol seperti ini:

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

Dan pastikan Anda memiliki yang berikut ini di ConfigureServices di Startup.cs:

services.AddDistributedMemoryCache();
egmfrs
sumber
0

Utas ini membantu saya membuat solusi saya sendiri yang akan saya bagikan di sini. Saya menggunakan permintaan GET ajax pada awalnya tanpa masalah tetapi sampai pada titik di mana panjang URL permintaan terlampaui jadi saya harus beralih ke POST.

Javascript menggunakan plugin unduhan file JQuery dan terdiri dari 2 panggilan yang berhasil. Satu POST (Untuk mengirim params) dan satu GET untuk mengambil 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)
    });

Sisi 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");
    }
Machinegon
sumber
0

Jawaban CSL diimplementasikan dalam proyek yang sedang saya kerjakan, tetapi masalah yang saya alami adalah penskalaan di Azure merusak unduhan file kami. Sebagai gantinya, saya dapat melakukan ini dengan satu panggilan 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);
}

CLIENT (versi modifikasi dari Tangani unduhan file dari posting 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));
});
wilsjd
sumber
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);
    }
rinku Choudhary
sumber
tidak dapat membuka file, excel hanya membuka dan tidak menutup sendiri, saya bahkan menambahkan stream.close () sebelum robj tetapi tidak berfungsi.
kode fajar
0

Saya mungkin terdengar sangat naif, dan mungkin menarik cukup banyak kritik, tetapi inilah cara saya melakukannya,
( Ini tidak melibatkan ajaxekspor, tetapi juga tidak melakukan postback penuh )

Terima kasih untuk ini posting dan ini jawabannya.
Buat pengontrol sederhana

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

Dan inilah Tampilannya ..

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

Inti dari trik Tampaknya, kami posting bentuk (a bagian dari Razor View) atas mana kita memanggil sebuah Action method, yang kembali: a FileResult, dan ini FileResultkembali the Excel File..
Dan untuk posting nilai-nilai filter, seperti kata, ( dan jika Anda perlu), saya membuat permintaan posting ke tindakan lain, seperti yang telah dicoba untuk dijelaskan ..

Irfaan
sumber
-1

Saya menggunakan Asp.Net WebForm dan saya hanya ingin mengunduh file dari sisi server. Ada banyak artikel tapi saya tidak bisa menemukan jawaban yang mendasar. Sekarang, saya mencoba cara dasar dan mendapatkannya.

Itu masalah saya.

Saya harus membuat banyak tombol input secara dinamis saat runtime. Dan saya ingin menambahkan setiap tombol ke tombol unduh dengan memberikan fileNumber yang unik.

Saya membuat setiap tombol seperti ini:

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

Setiap tombol memanggil metode ajax ini.

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

Kemudian saya menulis metode sederhana dasar.

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

Saya membuat Form_1, Form_2, Form_3 .... Dan saya akan menghapus file lama ini dengan program lain. Tetapi jika ada cara untuk hanya mengirim array byte untuk mengunduh file seperti menggunakan Respon. Saya ingin menggunakannya.

Semoga bermanfaat bagi siapapun.

Bisa OTUR
sumber
-1

On Kirim formulir

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();
        }
    }
Rajesh Kumar
sumber