HowTo: Excel Export mit ASP.NET MVC und "Render View To String"

imageMan kann relativ einfach auf seiner Seite einen Excel-Export bei tabellarischen Daten anbieten ohne großartige SDKs zu wälzen. Excel versteht von Haus aus auch HTML Tabellen. Man ist zwar eingeschränkt, aber es ist schnell gemacht. Bei einer ASP.NET MVC Anwendung wäre es nun noch schön, dass man das Markup der HTML Tabelle als View speichert. Über eine kleine Extension kann man sich den View auch als String ausgeben lassen.

Unser Ziel: Excel Export

Wir möchten auf unserer Webseite einen Excelexport anbieten. Etwas ähnliches, nur etwas kurioser gemacht, habe ich auch schonmal auf dem Blog geschrieben.

Was muss man dafür tun?

Im Grunde reicht es aus, wenn man in die Response eine HTML Tabelle rendert und als Contenttype “application/ms-excel” angibt. Wir möchten aber die Tabelle als View speichern um Sie einfacher editieren zu können und nicht hardcoded im Source Code zu pflegen.

RenderViewToString

Wir benötigen zu aller erst eine Komponente die einen View als String uns zurückgibt. Wichtig dabei ist, dass die Response während des Vorgangs nicht schon zum Client geschickt wird (Response.Flush()), da man ansonsten den Contenttyp nicht mehr ändern kann! Eine der Lösungen in diesem Thread hat auch funktioniert – in ASP.NET MVC 2.0 muss man allerdings eine kleine Sache ändern -> Siehe Stackoverflow.

/// <summary>Renders a view to string.</summary>
public static string RenderViewToString(this Controller controller,
                                        string viewName, object viewData) {
    //Create memory writer
    var sb = new StringBuilder();
    var memWriter = new StringWriter(sb);

    //Create fake http context to render the view
    var fakeResponse = new HttpResponse(memWriter);
    var fakeContext = new HttpContext(HttpContext.Current.Request, fakeResponse);
    var fakeControllerContext = new ControllerContext(
        new HttpContextWrapper(fakeContext),
        controller.ControllerContext.RouteData,
        controller.ControllerContext.Controller);

    var oldContext = HttpContext.Current;
    HttpContext.Current = fakeContext;

    //Use HtmlHelper to render partial view to fake context
    var html = new HtmlHelper(new ViewContext(fakeControllerContext,
        new FakeView(), new ViewDataDictionary(), new TempDataDictionary()),
        new ViewPage());
    html.RenderPartial(viewName, viewData);

    //Restore context
    HttpContext.Current = oldContext;    

    //Flush memory and return output
    memWriter.Flush();
    return sb.ToString();
}

/// <summary>Fake IView implementation used to instantiate an HtmlHelper.</summary>
public class FakeView : IView {
    #region IView Members

    public void Render(ViewContext viewContext, System.IO.TextWriter writer) {
        throw new NotImplementedException();
    }

    #endregion
}

Nun können wir über diese Methode einen String aus einem View erzeugen – schonmal nicht schlecht.

Jetzt kann man entweder direkt das ContentResult nutzen oder man baut sich sein eigens ExcelResult:

public class ExcelResult : ActionResult
    {
        public string FileName { get; set; }
        public string Content { get; set; }

        public ExcelResult(string filename, string content)
        {
            this.FileName = filename;
            this.Content = content;
        }

        public override void ExecuteResult(ControllerContext context)
        {
            WriteFile(this.FileName, "application/ms-excel", this.Content);
        }

        private static void WriteFile(string fileName, string contentType, string content)
        {

            HttpContext context = HttpContext.Current;
            context.Response.Clear();
            context.Response.AddHeader("content-disposition", "attachment;filename=" + fileName);
            context.Response.Charset = "";
            context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
            context.Response.ContentType = contentType;
            context.Response.Write(content);
            context.Response.End();
        }
    }

Die Anwendung

Folgender View ist unser Exceltemplate (mit einem ViewModel)

%@ Import Namespace="MvcRenderToString.Models"%>
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IList<ExcelData>>" %>

<table>
    <tr>
        <% foreach(ExcelData excel in Model) {%>
        <td><%=excel.Foobar %></td>
        <% } %>
    </tr>
</table>

Und der Controller:

        public ExcelResult Excel()
        {
            List<ExcelData> foobars = new List<ExcelData>();
            foobars.Add(new ExcelData() { Foobar = "HelloWorld!"});
            foobars.Add(new ExcelData() { Foobar = "HelloWorld!" });
            foobars.Add(new ExcelData() { Foobar = "HelloWorld!" });
            foobars.Add(new ExcelData() { Foobar = "HelloWorld!" });

            string content = this.RenderViewToString("Excel", foobars);
            return new ExcelResult("Foobar.xls", content);
        }

Das Ergebnis:

image

Einziges Manko ist, dass Excel dem nicht ganz vertraut. Leider ist das ByDesign so:

image

Bei Bestätigung auf “Ja”:

image

Fetzt, oder?

Weitere Anwendungsmöglichkeiten

Dadurch das wir eine HTML Tabelle benutzen, können wir die Tabelle auch genauso auf unserer normalen Webseite benutzen. Hierbei Rufe ich dann einfach RenderPartial etc. auf.

Man könnte so auch Email Templates als View ablegen. Die Möglichkeiten sind jedenfalls da :)

[ Download Democode ]


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.

4 Responses

  1. Im IE kann es zu Problemen mit dem Excelresult kommen. Wenn man direkt per GET auf die URL des Controllers geht mag der IE das File nicht runterladen.

    Lösung des Problems: Diese Zeile entfernen:
    context.Response.Cache.SetCacheability(HttpCacheability.NoCache);

    Reply
  2. YOu don’t understand POO of ASP MVC,

    public class ExcelResult : ActionResult
    {
    public ViewResult _result { get; set; }

    public ExcelResult(ViewResult result)
    {
    _result = result;
    }

    public override void ExecuteResult(ControllerContext context)
    {
    context.HttpContext.Response.AppendHeader(“content-disposition”, “attachment;filename=export.xls”);
    context.HttpContext.Response.ContentType = “application/ms-excel”;
    context.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
    _result.ExecuteResult(context);
    }
    }

    Reply
  3. Thanks for the config httpReponse ;)

    Reply

Comment on this post

Letzte Posts

  • image.png
    Chocolatey–apt-get für Windows

    Durch Zufall bin ich auf das Tool “Chocolatey” gestoßen. Wer die Website sich anschaut, wird evtl. eine Verwandschaft mit NuGet ausmachen. Was macht Chocolatey? Chocolatey ist ein “Maschine Package Manager”, das bedeutet, dass man für seine Maschine einfach Tools runterladen und Updaten kann – direkt über die Konsole. Was ist der Unterschied zu NuGet? NuGet ...

  • image.png
    SASS, LESS & Coffeescript in Visual Studio mit der Web Workbench

    CSS und Javascript sind die “kleinste” Schnittmenge von allen Browsern für die Erstellung von Web-Applikationen. Leider geht dabei etwas komfort verloren, daher lieben alle Webentwickler jQuery! SASS und LESS sind zwei Varianten, wie man “schöner” CSS schreiben kann und Coffeescript versucht Javascript Entwicklung zu vereinfachen. Aber immer der Reihe nach… Was ist SASS? SASS steht ...

  • image.png
    Code-Inside Sample nun auf GitHub: Google Code zu GitHub Migration

    Seit einiger Zeit habe ich Beispielcode auf Google Code bereitgestellt. Einfach nur noch weg von Google Code O-Ton damals war: Ich hatte mich für Google Code entschieden, weil ich hoffe dass früher oder später die Google Code Suche nutzbar ist und es dadurch wenigstens ein kleiner Mehrwert entsteht. Allerdings wirft es momentan noch ein Fehler. ...

  • image.png
    Windows-8-Hackathon @Night in Leipzig

    Hacken (=Entwickeln, nichts böswilliges!), Grillen und mitten in der Nacht fachsimpeln? Dann ist vielleicht der Windows-8-Hackathon was für dich. Der Hackathon wird vom 15. Juni (ab 19:00) bis zum 16. Juni (bis in die frühen Morgenstunden) in Leipzig stattfinden.  Mit dabei sind auch Darius Parys und Tom Wendel von der Microsoft Deutschland. Thematisch (wie der ...

  • image.png
    Einstieg in Redis on Windows & Redis mit .NET benutzen

    Redis gehört zu den NoSQL Datenbanken und ist dort in der Familie der Key-Value Stores zu finden. Redis wird oft mit “Blazing Fast” betitelt und laut dem Stackoverflow Thread soll es im Vergleich zu MongoDB zweimal (beim Schreiben) und sogar dreimal (beim Lesen) so schnell sein wie MongoDB – auch wenn der Vergleich etwas “hinkt” ...

Auf Amazon einkaufen & unterstützen

Facebook