HowTo: Excel-Export via AJAX

Wir (d.h. Oliver und ich) haben bei einem Projekt die Anforderung gehabt, in einer sehr AJAX und Javascriptlastigen Anwendung ein Excel Export mit einzubauen.

Durch ein Postback oder durch Aufruf einer Webmethode etc. ist dies sehr einfach zu lösen, allerdings gab es ein Problem:

Die letztendliche Ergebnisliste wird über verschiedene Javascript Methoden befüllt, d.h. das momentan angezeigte (und in einem JSON Objekt befindliche) Ergebnis muss am besten direkt irgendwie übergeben werden.

Da die Lösung etwas "geschickt" ist (der Erfinder ist Oliver) Schritt für Schritt erklärt:

1. Grundsätzlicher Aufbau

image

2. Default.aspx vorbereiten

Die ASPX Seite (abgesehen von dem Javascript – welches gleich näher beschrieben wird) ist simpel:

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
    <script type="text/javascript">
    // wird gleich gezeigt
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <button type="button" onclick="createXlsData()">Create XLS</button>
    </div>
    </form>
</body>
</html>

Zwischenbemerkung: Wie erstellt man eigentlich eine Excel List?

Excel kann auch HTML Tabellen "interpretieren" und entsprechend darstellen.

Das HTML drumherum muss man weg lassen, um eine einfache Tabelle zu erzeugen muss man Excel sowas übergeben:

<table>
  <tr>
     <td>Sample</td>
  </tr>
</table>

3. Im Javascript Tabelle vorbereiten: "createXlsData()"

In diesem Teil können wir nun auf unsere JSON Objekte zugreifen – ohne nochmal einen Webservice etc. zu befragen.

function createXlsData()
    {
    var data = "<table>";
            data += "<tr>";
                data += "<td>Test 1</td>";
                data += "<td>Test 2</td>";
             data += "</tr>"

    for(i=0; i < 10; i++)
    {
        var singleLine = "<tr>";
                singleLine += "<td> Test Data1: " + i + "</td>";
                singleLine += "<td> Test Data2: "  + i + "</td>";
        singleLine += "</tr>";
        data += singleLine;
    }
        data += "</table>";
    sendXlsData(data);
    }

4. Daten an Handler übermitteln und Handler aufrufen

Da der String für einen "GET" Request zu lang ist, muss er per "POST" an den Handler übermittelt werden . natürlich mit AJAX.

Problem: Wenn man dies per AJAX macht, sieht man nie dieses Fenster:

image

Lösung: Der Handler speichert die übergebenen Werte in der Session und das Javascript ruft expliziert nochmal den Handler aus, welcher die Daten aus der Session holt.

Hier der komplette Source-Code der Default.aspx:

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
    <script type="text/javascript">
    var DataExchangeHttpRequest;

    function createXlsData()
    {
    var data = "<table>";
            data += "<tr>";
                data += "<td>Test 1</td>";
                data += "<td>Test 2</td>";
             data += "</tr>"

    for(i=0; i < 10; i++)
    {
        var singleLine = "<tr>";
                singleLine += "<td> Test Data1: " + i + "</td>";
                singleLine += "<td> Test Data2: "  + i + "</td>";
        singleLine += "</tr>";
        data += singleLine;
    }
        data += "</table>";
    sendXlsData(data);
    }

    function sendXlsData(data)
    {
    if (window.XMLHttpRequest) // Mozilla, Safari, Opera, IE7
        {
        DataExchangeHttpRequest = new XMLHttpRequest();
        }
    else if (window.ActiveXObject) // IE6, IE5
        {
        DataExchangeHttpRequest = new ActiveXObject("Microsoft.XMLHTTP");
        }

    DataExchangeHttpRequest.onreadystatechange = openXlsPage;
    DataExchangeHttpRequest.open('POST', 'ExcelHandler.ashx', true);
    DataExchangeHttpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
    DataExchangeHttpRequest.setRequestHeader('Content-Length',data.length);

    DataExchangeHttpRequest.send(data);
    }
    function openXlsPage()
    {
        if (DataExchangeHttpRequest.readyState == 4 && DataExchangeHttpRequest.status == 200)
        {
        window.open("ExcelHandler.ashx?openXls=true","xls");
        }
    }
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <button type="button" onclick="createXlsData()">Create XLS</button>
    </div>
    </form>
</body>
</html>

5. Der ASHX Handler

<%@ WebHandler Language="C#" Class="ExcelHandler" %>

using System;
using System.Web;

public class ExcelHandler : IHttpHandler, System.Web.SessionState.IRequiresSessionState
{

    public void ProcessRequest(HttpContext context)
    {

        if (context.Request.Params["openXls"] == "true")
        {
            // display data
            if (context.Session["ExcelData"] != null)
            {
                context.Response.ContentType = "application/vnd.ms-excel";
                context.Response.ContentEncoding = System.Text.Encoding.Default;
                context.Response.Write(context.Session["ExcelData"]);
            }
            else
            {
                context.Response.Write("session empty");
            }
        }
        else
        {
            // save data in session
            context.Response.ContentType = "application/vnd.ms-excel";
            string data = "";
            using (System.IO.StreamReader reader = new System.IO.StreamReader(context.Request.InputStream))
            {
                data = reader.ReadToEnd();
            }

            context.Session.Add("ExcelData", data);
        }
    }
    public bool IsReusable {
        get {
            return true;
        }
    }

}

Durch den Content Type "application/vnd.ms-excel" wird diese Javascript-generierte Tabelle von Excel erkannt.

Schematische Darstellung

image

Als Nutzer sieht das dann so aus

Klicken:

image

Fenster öffnet sich:

image

Im Excel sieht das dann so aus:

image

Bei der momentanen Lösung kommt da allerdings am Anfang noch eine Warnung, dass eine ASHX versucht was an Excel zu schicken – das bekommt man sicherlich auch noch irgendwie raus.

Wo macht das Sinn?

Das ganze macht da Sinn, wo man mit viel Javascript und AJAX sich Daten z.B. in einem JSON Objekt gesammelt hat und diese nun auch an Excel exportieren möchte – was bei uns der Fall war. Wenn man natürlich die Daten aus einer DB holen kann, ist sowas natürlich über ein Postback oder wie auch immer "sauberer".

Das ganze gehört eindeutig in die Kategorie: Freakige Sachen ;)

[ Download Source ]


Kick It auf dotnet-kicks.de
Wenn dir der Blogpost gefallen hat, dann hinterlasse doch einen Kommentar. Wenn du auf dem Laufenden bleiben willst, abonniere unseren RSS Feed oder folge uns auf Twitter.

About the author

Written by Robert Mühsig

Robert Mühsig (@robert0muehsig) ist Webentwickler und beschäftigt sich mit Web-Frameworks (vor allem dem ASP.NET MVC Framework) und scheut sich auch nicht vor Javascript. Ansonsten bloggt er über all jene Probleme, die ihm über den Weg laufen. Seit 2008 ist er Microsoft MVP für ASP.NET und er arbeitet bei der T-Systems Multimedia Solutions GmbH in Dresden. Treffen kann man ihn online via Twitter (@robert0muehsig) oder dieser Seite oder bei der .NET User Group Dresden.

One Response

Comment on this post

Letzte Posts

  • Carriage Return / Neue Zeile in Textareas

    Eine kleine Aufgabe: Jede neue Textzeile (Carriage Return/Wenn man Enter drückt ) in einer Textarea soll ein Element in einer Auflistung sein – wie mach ich das jetzt am einfachsten? Eigentlich ein grundlegendes Element im Web und der Nutzer macht bewusst Absätze – daher wäre es nur gerecht, wenn man das auch entsprechend würdigt. Kleine ...

  • image.png
    Doom, Quake, Wolfenstein & co. Source Code auf GitHub

    id Software, die Macher von Doom, Quake, Wolfenstein & co., stellen regelmäßig ihre älteren Spieltitle als Open Source zur Verfügung. Das Ganze runterzuladen fand ich bisher immer recht mühselig, allerdings gibt es seit kurzer Zeit die Sourcen auch auf GitHub. Darunter Spiele wie Doom 3, Quake 3, Wolfenstein für iOS. Wer also schon immer mal ...

  • image.png
    Twitter Bootstrap 2.0 released & “Release Präsentation”

    Wie bereits vom Twitter Bootstrap Team angekündigt wurde offiziel die Version 2.0 des UI Toolskits “Twitter Bootstrap” veröffentlich. Zudem wurden die Slides, welche bei der Release Party gezeigt wurden auch veröffentlicht: Downloads finden sich auf der Twitter Bootstrap Seite auf GitHub. Wenn dir der Blogpost gefallen hat, dann hinterlasse doch einen Kommentar. Wenn du auf ...

  • image.png
    Javascript zu Dart Translator

    Dart, Google Javascript Alternative, wurde vor ein paar Monaten vorgestellt und die Webentwickler Szene ist noch etwas gespalten, ob Dart nun überflüssig ist oder einfach nur cool und längst überfällig ist. Um die Sprache näher zu erläutern hat Google die grundlegenden Javascript Basics nach Dart übersetzt. Das Ergebnis ist der “Translator”. Der Name mag momentan ...

  • Twitter Bootstrap 2.0–“Beta”

    Twitter Bootstrap, ein UI-Toolkit für Web-Applikationen von Twitter, erscheint (wie bereits berichtet) demnächst in der Version 2.0. Der offizielle Release ist am 31. Januar, allerdings beginnt jetzt laut Mark Otto (einer der Hauptentwickler von Twitter Bootstrap) die intensive Test-Phase. Das heisst, das es nun offiziel auch die 2.0 Dokumentation online gibt. Im Vergleich zur aktuellen ...

Support us!

Facebook