13

I used to export data to excel in asp.net mvc using below code

    Response.AppendHeader("content-disposition", "attachment;filename=ExportedHtml.xls");
    Response.Charset = "";
    Response.Cache.SetCacheability(HttpCacheability.NoCache);
    Response.ContentType = "application/vnd.ms-excel";
    this.EnableViewState = false;
    Response.Write(ExportDiv.InnerHtml);
    Response.End();

When this Code Run it create a file and ask for a location to save

I tried working with NPOI and create Excel file very well but cant save file on client location .

Is there any way to make above code works on asp.net core 2.0 or any other way where I can save data in excel format on client machine ?

Tanwer
  • 1,503
  • 8
  • 26
  • 41
  • Websites cannot unilaterally save files to visitor's disks. – Dai May 09 '18 at 06:58
  • Ok , than what we call this behaviour for asp.net , they do save – Tanwer May 09 '18 at 07:00
  • I used another library called EPPlus (https://github.com/JanKallman/EPPlus) to generate Excel files and turn them into binary before sending back to the client. In .Net Core (or Mvc in general), you can return File() from controller actions. – David Liang May 09 '18 at 07:03
  • I can but it needs to be tomorrow as I am going to bed soon. In the meanwhile, you can check out the sample the library has: https://github.com/JanKallman/EPPlus/tree/master/SampleWebApp.Core. – David Liang May 09 '18 at 07:13

4 Answers4

25

There are many ways to achieve that.

Option 1: save to wwwroot

You can generate the Excel and save it to the wwwroot folder. And then you can serve it as static content on the page.

For example you have a folder called 'temp' inside the wwwroot folder to contain all the newly generated excels.

<a href="\temp\development\user1\2018\5\9\excel1.xlsx" download>Download</a>

There are limitations on this approach. 1 of them is the new download attribute. It only works on modern browsers.

Option 2: byte array

Another way is to generate the Excel, convert it into byte array and send it back to the controller. For that I use a library called "EPPlus" (v: 4.5.1) which supports .Net Core 2.0.

The following is just some sample codes I put together to give you an idea. It's not production ready.

using OfficeOpenXml;
using OfficeOpenXml.Style;

namespace DL.SO.Web.UI.Controllers
{
    public class ExcelController : Controller
    {
        public IActionResult Download()
        {
            byte[] fileContents;

            using (var package = new ExcelPackage())
            {
                var worksheet = package.Workbook.Worksheets.Add("Sheet1");

                // Put whatever you want here in the sheet
                // For example, for cell on row1 col1
                worksheet.Cells[1, 1].Value = "Long text";

                worksheet.Cells[1, 1].Style.Font.Size = 12;
                worksheet.Cells[1, 1].Style.Font.Bold = true;

                worksheet.Cells[1, 1].Style.Border.Top.Style = ExcelBorderStyle.Hair;

                // So many things you can try but you got the idea.

                // Finally when you're done, export it to byte array.
                fileContents = package.GetAsByteArray();
            }

            if (fileContents == null || fileContents.Length == 0)
            {
                return NotFound();
            }

            return File(
                fileContents: fileContents,
                contentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                fileDownloadName: "test.xlsx"
            );
        }
    }
}
David Liang
  • 20,385
  • 6
  • 44
  • 70
  • Thanks , I got the idea , I am able to do this by breaking the whole process in two step . Step 1 is saving the excel file in wwwroot folder using NPOI and second step is using return File() statement to save it on client machine . Its work very well , Problem Solved . – Tanwer May 10 '18 at 04:07
  • Hi, the second one is a good approach, but how I download the file then in the frontend?? – Yuri Morales Sep 04 '18 at 15:28
  • @YuriMorales: Generate a url to call the `Download()` action in the `ExcelController`? For example, `Url.Action("download", "excel", new { area = "" })`? – David Liang Sep 04 '18 at 18:36
  • 1
    No, sorry forgot to mention is VueJs frontend, my backend is net core webapi, but I already solved the prolem with npm package file-saver. – Yuri Morales Sep 04 '18 at 18:43
  • no, do not allow people to generate XML and serve the content as static content from `wwwroot`! are you kidding!? – Chaim Eliyah Nov 22 '18 at 05:10
  • 1
    @ChaimEliyah: I am not saying it's the best option, but it is a working option. From my experience, sometimes due to company policy, you can't always save the files as bytes in database. – David Liang Nov 22 '18 at 09:25
  • I understand. But I'd write a nice validator around it to make sure you are only serving files uploaded by an authenticated human user, and that they don't contain standard attack patterns. – Chaim Eliyah Nov 23 '18 at 00:59
5

Agreed with David Liang's answer.

Slide modifications if want to export whole DataTable.

            string export="export";
            DataTable dt = new DataTable();
            //Fill datatable
            dt = *something*

            byte[] fileContents;
            using (var package = new ExcelPackage())
            {
                var worksheet = package.Workbook.Worksheets.Add(export);
                worksheet.Cells["A1"].LoadFromDataTable(dt, true);
                fileContents = package.GetAsByteArray();
            }
            if (fileContents == null || fileContents.Length == 0)
            {
                return NotFound();
            }
            return File(
                fileContents: fileContents,
                contentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                fileDownloadName: export + ".xlsx"
            );
Rohil Patel
  • 386
  • 3
  • 8
  • for people who wants to convert the List to Datatable: https://stackoverflow.com/questions/564366/convert-generic-list-enumerable-to-datatable – Cedric Arnould Sep 30 '19 at 18:48
1

Here is our solution to this:

Depends on EPPlus.Core/1.5.4

using OfficeOpenXml;

public class XmlService
{
    // [...]
    public void getXlsxFile(SomeTableObject tbl, ref byte[] bytes)
    {
        using (ExcelPackage pck = new ExcelPackage())
        {
            ExcelWorksheet ws = pck.Workbook.Worksheets.Add(tbl.name);
            ws.Cells["A1"].LoadFromDataTable(tbl, true);
            bytes = pck.GetAsByteArray();
        }
    }
}

More information on EPPlus is available here and the source code above can be found at our open source repo (GPL).

Chaim Eliyah
  • 2,743
  • 4
  • 24
  • 37
0

Just use this code instead of your own:

    Response.Clear();
    Response.ContentType = "application/vnd.ms-excel";
    Response.Headers[HeaderNames.ContentDisposition] = "attachment; filename=ExportedHtml.xls";
    Response.WriteAsync(sb.ToString()).Wait();