ASP.NET Web アプリケーションで Excelファイルをエクスポートする - ASP.NET

ASP.NET Web アプリケーションで Excelファイルをエクスポートするコードを紹介します。

概要

Webアプリケーションで結果などをExcelファイルでエクスポートする機能を実装したいことがあります。
この記事では、Open XML SDKを利用したExcelファイルのエクスポートの実装方法とコードを紹介します。

方針

Excelファイルを編集する場合、"Microsoft Excel Object Library" の Microsoft.Office.Interop.Excelアセンブリを参照して、 COMオブジェクトを利用してExcelファイルを編集する方法があります。(実装方法はこちらの記事を参照)
しかし、この方法はサーバーで利用するのは推奨されていないこと、COMオブジェクトのメモリ開放が複雑でメモリリークの原因になりやすいことからWebアプリケーションで利用するのは不適切です。
別の方法として、Open XML SDKを利用してExcelファイルの操作をする方法があります。この方法を利用するとWebアプリケーションでExcelファイルを出力することが比較的容易にできます。

事前準備

Open XML SDKをダウンロードしてプロジェクトにインストールします。詳しい手順はこちらの記事を参照してください。

プログラム

今回はASP.NET Coreアプリケーションで作成します。ASP.NET WebForm アプリケーションで実装する場合は、ジェネリックハンドラー(*.ashx)を利用して同様に実装できます。

コード

下記のコードを記述します。(記載のないASP.NET Coreアプリケーションのほかのファイルは編集していません。)
Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace ExcelFileExport
{
  public class Startup
  {
    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
      if (env.IsDevelopment()) {
        app.UseDeveloperExceptionPage();
      }

      app.UseRouting();

      app.UseEndpoints(endpoints =>
      {
        endpoints.MapGet("/excel", async context =>
        {
          ExcelExport ee = new ExcelExport();
          await ee.Export(context);
        });


        endpoints.MapGet("/", async context =>
              {
            await context.Response.WriteAsync("Hello World!");
          });
      });
    }
  }
}
ExcelExport.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using System.IO;

namespace ExcelFileExport
{
  public class ExcelExport
  {
    List<DataItem> dataList = new List<DataItem>();
    class DataItem
    {
      public int id;
      public string name;
      public string code;
      public int price;

    }

    public async Task Export(HttpContext context)
    {

      dataList.Clear();
      dataList.Add(new DataItem() { id = 1, name = "ぺんぎんクッキー", code = "PCD-001", price = 180 });
      dataList.Add(new DataItem() { id = 2, name = "あひるサブレ", code = "PCD-002", price = 120 });
      dataList.Add(new DataItem() { id = 3, name = "らくだキャラメル", code = "PCD-003", price = 40 });
      dataList.Add(new DataItem() { id = 4, name = "しろくまアイス", code = "PCA-001", price = 230 });
      dataList.Add(new DataItem() { id = 5, name = "だちょうカステラ", code = "PCD-004", price = 90 });

      //シート作成
      MemoryStream ms = new MemoryStream();
      SpreadsheetDocument doc = SpreadsheetDocument.Create(ms, SpreadsheetDocumentType.Workbook);
 
      WorkbookPart wbpart = doc.AddWorkbookPart();
      wbpart.Workbook = new Workbook();
      WorksheetPart wspart = wbpart.AddNewPart<WorksheetPart>();
      wspart.Worksheet = new Worksheet(new SheetData());
      Sheets sheets = doc.WorkbookPart.Workbook.AppendChild<Sheets>(new Sheets());

      Sheet sheet = new Sheet();
      sheet.Id = doc.WorkbookPart.GetIdOfPart(wspart);
      sheet.SheetId = 1;
      sheet.Name = "商品";
      sheets.Append(sheet);


      //Column
      Columns lstColumns = wspart.Worksheet.GetFirstChild<Columns>();
      if (lstColumns == null) {
        lstColumns = new Columns();
        wspart.Worksheet.InsertAt(lstColumns, 0);
      }

      lstColumns.Append(new Column() { Min = 1, Max = 1, Width = 4, CustomWidth = true });
      lstColumns.Append(new Column() { Min = 2, Max = 2, Width = 25, CustomWidth = true });
      lstColumns.Append(new Column() { Min = 3, Max = 4, Width = 12, CustomWidth = true });

      //データ挿入
      Row row;
      UInt32Value row_index = 1;

      foreach (DataItem di in dataList) {
        row = new Row() { RowIndex = row_index };
        wspart.Worksheet.GetFirstChild<SheetData>().Append(row);

        Cell refCell = null;
        Cell newCell;

        newCell = new Cell() { CellReference = "A" + row_index.ToString() };
        newCell.CellValue = new CellValue(di.id.ToString());
        newCell.DataType = new EnumValue<CellValues>(CellValues.Number);
        row.InsertAfter(newCell, refCell);
        refCell = newCell;
        //
        newCell = new Cell() { CellReference = "B" + row_index.ToString() };
        newCell.CellValue = new CellValue(di.name);
        newCell.DataType = new EnumValue<CellValues>(CellValues.String);
        row.InsertAfter(newCell, refCell);
        refCell = newCell;
        //
        newCell = new Cell() { CellReference = "C" + row_index.ToString() };
        newCell.CellValue = new CellValue(di.code);
        newCell.DataType = new EnumValue<CellValues>(CellValues.String);
        row.InsertAfter(newCell, refCell);
        refCell = newCell;
        //
        newCell = new Cell() { CellReference = "D" + row_index.ToString() };
        newCell.CellValue = new CellValue(di.price.ToString());
        newCell.DataType = new EnumValue<CellValues>(CellValues.Number);
        row.InsertAfter(newCell, refCell);
        refCell = newCell;

        row_index++;
      }

      wbpart.Workbook.Save();
      doc.Close();

      ms.Seek(0, SeekOrigin.Begin);
      byte[] buffer = new byte[ms.Length];
      await ms.ReadAsync(buffer,0,(int)ms.Length);
      ms.Close();

      context.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
      context.Response.Headers.Append("Content-Disposition", "attachment; filename=\"export.xlsx\"");
      await context.Response.Body.WriteAsync(buffer, 0, buffer.Length);

    }

  }
}

解説

Startup.cs

endpoints.MapGet()メソッドを実装します。アプリケーションルートURL/excel のURLにアクセスした場合に、Excelのファイルのダウンロードを開始します。
ダウンロードの処理は ExcelExport クラスに実装します。
    endpoints.MapGet("/excel", async context =>
    {
      ExcelExport ee = new ExcelExport();
      await ee.Export(context);
    });

ExcelExport.cs

Excelファイルの作成とダウンロードを実行します。Opne XML SDK ファイルを利用したExcelファイルの生成についてはこちらの記事を参照してください。今回の例では列幅も変更しています。列幅の変更はこちらの記事を参照してください。

下記のデータ作成部分は実際のアプリケーションでは計算結果やデータベースから取得した結果を設定します。今回はダミーデータを挿入しています。
  dataList.Add(new DataItem() { id = 1, name = "ぺんぎんクッキー", code = "PCD-001", price = 180 });
  dataList.Add(new DataItem() { id = 2, name = "あひるサブレ", code = "PCD-002", price = 120 });
  dataList.Add(new DataItem() { id = 3, name = "らくだキャラメル", code = "PCD-003", price = 40 });
  dataList.Add(new DataItem() { id = 4, name = "しろくまアイス", code = "PCA-001", price = 230 });
  dataList.Add(new DataItem() { id = 5, name = "だちょうカステラ", code = "PCD-004", price = 90 });

Excelのスプレッドシートのドキュメントは一般的にはファイルに作成しますが、今回はファイルをダウンロードしますのでMemoryStreamに保存します。
  MemoryStream ms = new MemoryStream();
  SpreadsheetDocument doc = SpreadsheetDocument.Create(ms, SpreadsheetDocumentType.Workbook);

ワークブックの保存後スプレッドシートを閉じます。
メモリストリームのシーク位置を先頭に移動し、byte配列のバッファにデータを読み出します。
  wbpart.Workbook.Save();
  doc.Close();

  ms.Seek(0, SeekOrigin.Begin);
  byte[] buffer = new byte[ms.Length];
  await ms.ReadAsync(buffer,0,(int)ms.Length);
  ms.Close();

byte配列のバッファの内容をレスポンスデーターとして書き込みます。書き込みにはResponse.Body.WriteAsync() メソッドを利用します。
Open XML のドキュメントのため、ContentTypeは application/vnd.openxmlformats-officedocument.spreadsheetml.sheet を指定しています。 また、今回はファイルをダウンロードしますので、Content-Disposition の値は attachment に設定し、filenameフィールドを指定しています。ファイル名は export.xls としています。
  context.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
  context.Response.Headers.Append("Content-Disposition", "attachment; filename=\"export.xlsx\"");
  await context.Response.Body.WriteAsync(buffer, 0, buffer.Length);

実行結果

プロジェクトを実行します。Webブラウザが起動しアプリケーションルートのページが表示されます。"Hello World!" のテキストが表示されます。


(アプリケーションルートURL)/excel URLを開きます。


URLにアクセスすると、ファイルのダウンロードが始まります。ファイル名が "export.xlsx" となっていることが確認できます。


ダウンロードしたExcelのファイルを開きます。値が入力されているExcelファイルがダウンロードできていることが確認できます。


ASP.NET アプリケーションでExcelファイルをエクスポートする処理が実装できました。

参考 : ASP.NET WebFormアプリケーションでの実装例 (ジェネリックハンドラーを利用)

ASP.NET WebFormアプリケーションでの実装例です。
excel.ashx.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace ExcelFileExportWebForm
{
  /// <summary>
  /// Handler1 の概要の説明です
  /// </summary>
  public class Handler1 : IHttpHandler
  {

    public void ProcessRequest(HttpContext context)
    {
      ExcelExport ee = new ExcelExport();
      ee.Export(context);
    }

    public bool IsReusable
    {
      get
      {
        return false;
      }
    }
  }
}
ExcelExport.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using System.IO;

namespace ExcelFileExportWebForm
{
  public class ExcelExport
  {
    List<DataItem> dataList = new List<DataItem>();
    class DataItem
    {
      public int id;
      public string name;
      public string code;
      public int price;

    }

    public void Export(HttpContext context)
    {

      dataList.Clear();
      dataList.Add(new DataItem() { id = 1, name = "ぺんぎんクッキー", code = "PCD-001", price = 180 });
      dataList.Add(new DataItem() { id = 2, name = "あひるサブレ", code = "PCD-002", price = 120 });
      dataList.Add(new DataItem() { id = 3, name = "らくだキャラメル", code = "PCD-003", price = 40 });
      dataList.Add(new DataItem() { id = 4, name = "しろくまアイス", code = "PCA-001", price = 230 });
      dataList.Add(new DataItem() { id = 5, name = "だちょうカステラ", code = "PCD-004", price = 90 });

      //シート作成
      MemoryStream ms = new MemoryStream();
      SpreadsheetDocument doc = SpreadsheetDocument.Create(ms, SpreadsheetDocumentType.Workbook);

      WorkbookPart wbpart = doc.AddWorkbookPart();
      wbpart.Workbook = new Workbook();
      WorksheetPart wspart = wbpart.AddNewPart<WorksheetPart>();
      wspart.Worksheet = new Worksheet(new SheetData());
      Sheets sheets = doc.WorkbookPart.Workbook.AppendChild<Sheets>(new Sheets());

      Sheet sheet = new Sheet();
      sheet.Id = doc.WorkbookPart.GetIdOfPart(wspart);
      sheet.SheetId = 1;
      sheet.Name = "商品";
      sheets.Append(sheet);


      //Column
      Columns lstColumns = wspart.Worksheet.GetFirstChild<Columns>();
      if (lstColumns == null) {
        lstColumns = new Columns();
        wspart.Worksheet.InsertAt(lstColumns, 0);
      }

      lstColumns.Append(new Column() { Min = 1, Max = 1, Width = 4, CustomWidth = true });
      lstColumns.Append(new Column() { Min = 2, Max = 2, Width = 25, CustomWidth = true });
      lstColumns.Append(new Column() { Min = 3, Max = 4, Width = 12, CustomWidth = true });



      //データ挿入
      Row row;
      UInt32Value row_index = 1;

      foreach (DataItem di in dataList) {
        row = new Row() { RowIndex = row_index };
        wspart.Worksheet.GetFirstChild<SheetData>().Append(row);

        Cell refCell = null;
        Cell newCell;

        newCell = new Cell() { CellReference = "A" + row_index.ToString() };
        newCell.CellValue = new CellValue(di.id.ToString());
        newCell.DataType = new EnumValue<CellValues>(CellValues.Number);
        row.InsertAfter(newCell, refCell);
        refCell = newCell;
        //
        newCell = new Cell() { CellReference = "B" + row_index.ToString() };
        newCell.CellValue = new CellValue(di.name);
        newCell.DataType = new EnumValue<CellValues>(CellValues.String);
        row.InsertAfter(newCell, refCell);
        refCell = newCell;
        //
        newCell = new Cell() { CellReference = "C" + row_index.ToString() };
        newCell.CellValue = new CellValue(di.code);
        newCell.DataType = new EnumValue<CellValues>(CellValues.String);
        row.InsertAfter(newCell, refCell);
        refCell = newCell;
        //
        newCell = new Cell() { CellReference = "D" + row_index.ToString() };
        newCell.CellValue = new CellValue(di.price.ToString());
        newCell.DataType = new EnumValue<CellValues>(CellValues.Number);
        row.InsertAfter(newCell, refCell);
        refCell = newCell;

        //row.InsertBefore(

        row_index++;
      }

      wbpart.Workbook.Save();
      doc.Close();

      ms.Seek(0, SeekOrigin.Begin);
      byte[] buffer = new byte[ms.Length];
      ms.ReadAsync(buffer, 0, (int)ms.Length);
      ms.Close();

      context.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
      context.Response.Headers.Add("Content-Disposition", "attachment; filename=\"export.xlsx\"");
      context.Response.BinaryWrite(buffer);

    }
  }
}
著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
最終更新日: 2021-05-16
作成日: 2020-10-18
iPentec all rights reserverd.