Question

I need to make an 'export to excel' feature.

Excel is a binary file, and it sends a warning on msExcel if you use its XML format.

What i'm currently doing is keeping a state object (since i need to be aware of the various changes a user is making) with all my data and passing it to a .net MVC controller using ajax, which uses a library to create the *.xlsx file.

client side:

 $.ajax({
    type: "POST",
    url: "/path/to/exportController",
    data: exporter.getData(),
    success: success,
    error: error,
    dataType: 'json'
 });

Server side:

 public ActionResult Excel(mySpecialandIrrelevantDescriptiveClass jsonData)
    {
      //excel generation logic
      .....
    }

Now from the way i see it, i got 3 options here:

  1. Create a stream and push it back to the ajax request - but from what i've gathered, ajax can't really push for a download request with a generated binary stream

  2. save the file on the servers hard-drive, return a public url for it and open a new popup window with the new URL, get the download prompt and somehow close the window so it wouldn't look ugly.

  3. forget about the ajax, push everything into some hidden form fields and post it to the server, again opening a new window and wait for the response , then closing it.

option #2 will require me to create a task that will constantly purge the generated files and URLs which is a huge hassle since the platform will get about 10-20k hits for this feature alone.

What's the best way to achieve what i need?

Was it helpful?

Solution

So this is how i eventually got it :

You can't use ajax to return a binary file - you'll receive its data - but will have no way of manifesting the binary file

I've used EPPlus as the excel library and passed the data through a post method within generic HTML form hidden fields. The output was generated using a stream as the return value thus keeping the user on the page without refreshes/redirects

Server:

//the expected data description - automatic tool @ http://json2csharp.com/
public class ExpectedJSON
{
    public class SomeDataSet
    {
        public string dude{ get; set; }
        public string what { get; set; }
        public string foo { get; set; }
        public string bar { get; set; }
        public string baz { get; set; }
        public int asd { get; set; }
        public string someDate { get; set; }
        public string wat { get; set; }
        public string grrr { get; set; }
    }

    public class AnotherDataSet
    {
        public int type { get; set; }
        public string date { get; set; }
        public string dancing { get; set; }
        public double camels { get; set; }
        public int are { get; set; }
        public int crying { get; set; }
        public double _for { get; set; }
        public double some { get; set; }
        public double beer { get; set; }
    }

    public class MoreData
    {
        public int dark { get; set; }
        public double side { get; set; }
        public int of { get; set; }
        public int the { get; set; }
        public double moon { get; set; }
        public double iz { get; set; }
        public double da { get; set; }
        public string bomb { get; set; }
    }
}

public class ExportToController : Controller
{
    public ActionResult Excel([FromUri(Name= "someData")] string someDataSet,
                              [FromUri(Name = "anotherData")] string anotherDataSet,
                              [FromUri(Name = "moreData")] string moreData)
    {
        ExpectedJSON.SomeDataSet someDataSetJson = JsonConvert.DeserializeObject<ExpectedJSON.SomeDataSet>(someData);
        ExpectedJSON.AnotherDataSet[] anotherDataSetJson = JsonConvert.DeserializeObject<Campaign.OnlineComulativeDailyBuildChart[]>(anotherData);
        ExpectedJSON.MoreData[] moreDataJson = JsonConvert.DeserializeObject<Campaign.OnlineSitesGrid[]>(moreData);
        string fileName = "file.xlsx";

        ExcelPackage p = new ExcelPackage();
        p.Workbook.Worksheets.Add("Some Sheet");
        ExcelWorksheet ws = p.Workbook.Worksheets[1];

        //excel library logic follows...

        //...
        //..


       //stream logic

        MemoryStream stream = new MemoryStream(p.GetAsByteArray());


        FileStreamResult result = new FileStreamResult(stream, "application/vnd.ms-excel")
        {
            FileDownloadName = fileName
        };

        return result;
    }
}

HTML:

<form id="exportToExcel" action="../ExportTo/Excel/" target="_blank" method="post">
  <input type="hidden" id="someData" name="someData" />
  <input type="hidden" id="anotherData" name="anotherData" />
  <input type="hidden" id="moreData" name="moreData" />
  <button type="submit">export</button>
</form>

All my data components (which needed to be exported) sign their data objects into a generic object so that on submit - the value attribute is populated with the json representation of it using JSON.stringify:

function bindEvents() {
    var exportForm = $("#exportToExcel");
    var someData= exportForm.find("#someData");
    var anotherData= exportForm.find("#anotherData");
    var moreData = exportForm.find("#moreData");
    exportForm.on('submit', function (e) {
        someData.attr("value", JSON.stringify(exporter.getData().someData));
        anotherData.attr("value", JSON.stringify(exporter.getData().anotherData));
        moreData .attr("value", JSON.stringify(exporter.getData().moreData));
    });
}

Minutes with rails/sinatra/nodeJS - days/weeks with .net - but there you have it.

OTHER TIPS

The are tons of questions like this:

I could go on with this :)

Bottom line they all suggest using some kind of redirection through a <form>, an <iframe> or window.location.

This is probably the way to go, however if you're feeling adventurous enough I would also try some combination of HTML5 Blob API, File System and/or Typed Arrays.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top