0

I use an html table where it's content can be changed with mouse drag and drop implemented. Technically, you can move the data from any table cell to another. The table size 50 row * 10 column with each cell given a unique identifier. I want to export it to .xlsx format with C# EPPlus library, and give back the exported file to client.

So I need the pass the whole table data upon a button press and post it to either a web api or an mvc controller, create an excel file (like the original html table data) and send it back to download with browser.

So the idea is to create an array which contains each of table cell's value ( of course there should be empty cells in that array), and post that array to controller.

The problem with that approach lies in the download, if I call the api or mvc controller with regular jquery's ajax.post it did not recognize the response as a file.

C# code after ajax post:

  [HttpPost]
    public IHttpActionResult PostSavedReportExcel([FromBody]List<SavedReports> savedReports, [FromUri] string dateid)
    {
        //some excel creation code
                          HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StreamContent(new MemoryStream(package.GetAsByteArray()))

            };
            response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
            {
                FileName = dateid + "_report.xlsx"
            };
            ResponseMessageResult responseMessageResult = ResponseMessage(response);
            return responseMessageResult;
    }

Usually, for this kind of result I could use window.location = myurltocontroller to download properly , but that is only for GET requests, POST anything is not possible.

I found some answers which could help me in this topic: JavaScript post request like a form submit

This points out I should go with creating a form, which passes the values, but I do not know how to do so in case of arrays (the table consists 50*10 = 500 values which I have to pass in the form)

I tried some only frontend solutions to the html-excel export problem, which of course does not require to build files on api side, but free jquery add-ins are deprecated, not customizeable, handle only .xls formats, etc.

I found EPPlus nuget package a highly customizeable tool, that is why I want to try this is at first place.

So the question is: how can I post an array of 500 elements, that the controller will recognize, generate the file, and make it automatically download from browser?

If you can provide some code that would be fantastic, but giving me the right direction is also helpful.

Thank you.

Newbie1001
  • 131
  • 11
  • 1
    Are you sure you need to go to the backend for this? There are JS libs that can export to XLSX: https://sheetjs.com/ or https://github.com/clarketm/TableExport – Efrain Oct 26 '20 at 11:20
  • 1
    You can download a file ajax response by creating an `a` with href `URL.createObjectURL` and clicking it: https://stackoverflow.com/a/23797348/2181514 – freedomn-m Oct 26 '20 at 11:34
  • @Efrain It would be the best if I do not. To be honest, I tried table2excel js lib first, but Excel reformat the values in the generated .xls file. For example, sometimes handle integer cell values as date, etc., which i do not know where to customize. Users reported this behaviour as a bug, even if it can be solved easily with minimal Excel knowledge. I will check these links, but I am afraid these libraries also lacks cell value format customization. – Newbie1001 Oct 26 '20 at 11:38
  • @freedomn-m This looks very promising! I will try it and share the results. I wonder how I missed this one after searching. Thank you. – Newbie1001 Oct 26 '20 at 11:41
  • 1
    No worries - there are a lot of question on the subject, most of them say it's not possible - but I was on a similar question (relatively) recently someone showed an `a` with a `data:` url created dynamically - the one above was one I just found – freedomn-m Oct 26 '20 at 11:44
  • Thank you @freedomn-m. Your solution at this link solved the problem. – Newbie1001 Oct 26 '20 at 14:27

1 Answers1

1

You can use fetch() (docs) to send the request from the JS frontend. When the browser (JS) has received the response, it can then offer its binary content as a download. Something like this:

fetch("http://your-api/convert-to-excel", // Send the POST request to the Backend
    {
        method:"POST",
        body: JSON.stringify(
            [[1,2],[3,4]] // Here you can put your matrix
        )
    })
    .then(response => response.blob())
    .then(blob => {
        // Put the response BLOB into a virtual download from JS
        if (navigator.appVersion.toString().indexOf('.NET') > 0) {
            window.navigator.msSaveBlob(blob, "my-excel-export.xlsx");
        } else {
            var a = window.document.createElement('a');
            a.href = URL.createObjectURL(blob);
            a.download = "my-excel-export.xlsx";
            a.click();
        }});

So the JS part of the browser actually first downloads the file behind the scenes, and only when it's done, it's triggering the "download" from the browsers memory into a file on the HD. This is a quite common scenario with REST APIs that require bearer token authentication.

Efrain
  • 3,248
  • 4
  • 34
  • 61
  • Thank you, I will try this in a short time and share my results. – Newbie1001 Oct 26 '20 at 14:31
  • I got an error on js side: Failed to execute createObjectURL(blob): overload resolution failed. Also, I set content-type to json, and replace 'result' with 'blob' in your code to got response. Strange thing is, what freedomn-n posted in comment is a very similar solution here: stackoverflow.com/a/23797348/2181514 ,and that worked. I don't know what went wrong here. Any ideas? We used Google Chrome browser in both cases. – Newbie1001 Oct 26 '20 at 15:45
  • 1
    I forgot to add the line `.then(response => response.blob())` which extracts the content body from the response (as blob). – Efrain Oct 26 '20 at 16:23
  • Ok, I will try it asap. :) – Newbie1001 Oct 27 '20 at 07:49
  • Worked as it should. Great! :) – Newbie1001 Oct 27 '20 at 07:54