C#アプリでExcelファイルのエクスポート/インポートを実装したい一方で、EPPlusの商用ライセンスが気になり、Officeが入っていないサーバーやPCでも動く方法を探している方向けに、無料で使える主要ライブラリと選び方、実装の落とし穴をまとめます。
結論:迷ったらClosedXML、要件が尖っているならOpen XML SDK
質問内容(Office不要/Excel入出力/セルコメント/列幅自動調整/商用でも無料で使いたい)をまとめて満たしやすいのは、まずClosedXMLです。ClosedXMLはMITライセンスで、Excel 2007+(.xlsx/.xlsm)を扱える高レベルAPIが揃っており、コメント付与や列幅調整までコードが短くなります。
一方で、Open XML SDKはMicrosoft系の公式ルートに近い立ち位置で、ファイル構造(Open XML)を直接操作できます。その分、実装は低レベルになり、コメントや自動列幅のような「Excelっぽい機能」を同じ手軽さで書くのは難しくなります。
| 観点 | ClosedXML | Open XML SDK |
|---|---|---|
| ライセンス | MIT(商用利用しやすい) | MIT(商用利用しやすい) |
| Office不要 | 不要(.xlsx/.xlsmを直接操作) | 不要(.xlsxのOpen XMLを直接操作) |
| 実装難易度 | 低(直感的なAPI) | 中〜高(XML構造の理解が必要) |
| セルコメント | APIで追加しやすい | 可能だが部品追加が多く複雑 |
| 列幅の自動調整 | AdjustToContentsで実装しやすい | 自前実装が基本(自動計測は作り込み) |
前提:Office(Interop)に頼らないのが安全な理由
「Officeがインストールされていない環境でも動く必要がある」という条件がある場合、Microsoft.Office.Interop.Excel(Excelの自動操作)に依存する設計は避けるのが定石です。加えて、Microsoftは非対話・無人実行(サービス、IIS等)でのOffice自動化を推奨・サポートしていない旨を明記しています。サーバー用途での安定性(ハング、デッドロック等)を考えると、Open XML(.xlsx)を直接読み書きするライブラリが現実的です。
ClosedXMLやOpen XML SDKは、Excelアプリを起動せずに、Open XML形式(.xlsx)を操作します。これにより「Officeがない環境」でも実行できます。
EPPlusのライセンスが不安な理由(要点だけ)
EPPlusはバージョン5以降、コミュニティ向けはPolyform Noncommercial(非商用向け)を中心にしたモデルになっており、商用利用では商用ライセンスが必要になるケースがあります。過去バージョンはLGPLの範囲だった、という履歴もあります。ここが「商用で無料のまま使って良いのか?」という不安に直結します。
ライセンス判断はプロジェクトの利用形態で変わるため、最終確認は必須ですが、「最初から商用でも使いやすいOSSライセンス(MIT/Apache-2.0等)の代替を選ぶ」という方針が現場では事故が少ないです。
ClosedXMLを第一候補にする理由
ライセンスと対応形式
ClosedXMLはMITライセンスで公開されており、一般に商用利用でも採用しやすい条件です。
また、ClosedXMLはExcel 2007+のOpen XML形式を対象にしており、主に.xlsx / .xlsmを扱います。古い.xls(バイナリ形式)には原則対応しないため、要件に「.xlsも来る」がある場合は後述のNPOIやExcelDataReaderとの併用を検討してください。
ClosedXMLで「やりたいこと」が揃うポイント
- ブック/シート作成、セルの読み書きが直感的
- セルコメント(ノート)をコードで追加できる
- 列幅を内容に合わせて調整(AutoFit相当)できる
- Excelアプリ不要で.xlsxを生成・編集できる
コメント追加は、たとえば ws.Cell(row, col).Comment.AddText(...) のように書けます。
列幅の自動調整は ws.Columns().AdjustToContents() で実行できます。
インストール(NuGet)
開発環境ではNuGetから導入します。CLIなら次の通りです。
dotnet add package ClosedXML
エクスポート例:データ書き込み+コメント+列幅自動調整
「見出し行 → データ行 → コメント → 列幅調整 → 保存」の順番にすると、AutoFitが期待通り効きやすいです(後述の注意点も参照)。
using ClosedXML.Excel;
public static class ExcelExportSample
{
public static void Export(string path)
{
using var wb = new XLWorkbook();
var ws = wb.Worksheets.Add("Data");
// 見出し
ws.Cell(1, 1).Value = "ID";
ws.Cell(1, 2).Value = "Name";
ws.Cell(1, 3).Value = "Age";
// 見出しの見た目(例)
ws.Range(1, 1, 1, 3).Style.Font.Bold = true;
ws.Range(1, 1, 1, 3).Style.Fill.BackgroundColor = XLColor.LightGray;
// データ(例)
var data = new[]
{
new { Id = 1, Name = "Alice", Age = 29 },
new { Id = 2, Name = "Bob", Age = 34 },
new { Id = 3, Name = "Chika", Age = 41 },
};
var row = 2;
foreach (var item in data)
{
ws.Cell(row, 1).Value = item.Id;
ws.Cell(row, 2).Value = item.Name;
ws.Cell(row, 3).Value = item.Age;
row++;
}
// 任意セルにコメント(ノート)を付ける
ws.Cell(2, 2).Comment.AddText("例:氏名は正式名称で入力してください");
// 列幅を内容に合わせて調整(AutoFit相当)
ws.Columns().AdjustToContents();
// 保存
wb.SaveAs(path);
}
}
インポート例:Excel → アプリへ取り込む(行の読み取り)
インポートは「どこまでをデータ行と見なすか」「空行が混ざる」「数値・日付が型ブレする」といった実務の罠が多いので、RangeUsed(使用範囲)を軸にしつつ、ヘッダー行で列を確定してから読む設計が安定します。
using ClosedXML.Excel;
public sealed record PersonRow(int Id, string Name, int? Age);
public static class ExcelImportSample
{
public static List<PersonRow> Import(string path)
{
using var wb = new XLWorkbook(path);
var ws = wb.Worksheet("Data");
var used = ws.RangeUsed();
if (used is null) return new List<PersonRow>();
// 1行目がヘッダー前提
var rows = used.RowsUsed().Skip(1);
var result = new List<PersonRow>();
foreach (var r in rows)
{
// 空行スキップ例
if (r.CellsUsed().All(c => c.IsEmpty())) continue;
var id = r.Cell(1).GetValue<int>();
var name = r.Cell(2).GetString();
// Ageは空欄があり得る想定
int? age = null;
var ageCell = r.Cell(3);
if (!ageCell.IsEmpty())
{
// 型が揺れても落ちないように、まず文字列で拾ってTryParseするのも手
if (int.TryParse(ageCell.GetString(), out var tmp))
age = tmp;
else
age = ageCell.GetValue<int>(); // 期待通りならこちらでOK
}
result.Add(new PersonRow(id, name, age));
}
return result;
}
}
テンプレート方式が強い(見た目とロジックを分離)
帳票や定型レイアウトがある場合、最初からExcelで「テンプレート(.xlsx)」を作っておき、C#側はセル値だけ差し込む方式が最も運用が安定します。ClosedXMLは既存ファイルを開いて編集できるため、テンプレート運用と相性が良いです。
using ClosedXML.Excel;
public static class TemplateFillSample
{
public static void Fill(string templatePath, string outputPath, string customerName, DateTime date)
{
using var wb = new XLWorkbook(templatePath);
var ws = wb.Worksheet(1);
ws.Cell("B2").Value = customerName;
ws.Cell("B3").Value = date;
wb.SaveAs(outputPath);
}
}
列幅自動調整(AdjustToContents)を実運用で安定させるコツ
- データを書き終えてからAdjustToContentsを呼ぶ(ヘッダー直後に呼ぶと期待通りにならないことがある)
- 全列ではなく、必要な列だけにかける(例:
ws.Columns(1, 10).AdjustToContents();) - 大規模データでは、測定行数を絞る(例:先頭数百行のみ)
AdjustToContentsは便利ですが、行数が多いと処理が重くなるケースが報告されています。大量データの全列AutoFitを毎回行うより、「主要列だけ」「先頭N行だけ」「最大幅を制限」などのルール化が現場向きです。
また、Linux環境でAdjustToContentsを使う場合は、フォント計測やグラフィックエンジンの前提(フォントの存在など)でエラーになることがあります。ClosedXML側はテキスト計測にフォントを使う設計があり、環境依存が出るため、サーバーOSにフォントを用意する・フォールバックフォント設定を検討する、といった対策が必要になる場合があります。
パフォーマンスとメモリの現実
ClosedXMLは「Excelをコードで扱いやすくする」ことに強い一方、巨大ファイル(数十万行など)ではメモリ消費や処理時間が課題になりやすい領域があります。操作をセル単位でループし続けるより、範囲操作を使う、不要な挿入削除を避けるなど、パフォーマンスガイドに沿った書き方が効きます。
Open XML SDK:公式寄りで「低レベルに確実」だが、実装量は増える
ライセンスと位置づけ
Open XML SDK(NuGet: DocumentFormat.OpenXml)はMITライセンスで提供されており、Office Open XML(.xlsx/.docx/.pptx)を直接操作できます。
Open XML SDKが向くケース
- 「ClosedXMLの抽象化が邪魔」なくらい、細かくファイル構造を制御したい
- 生成物のOpen XML構造(パーツ構成や最適化)を意識して作り込みたい
- 特定の仕様・社内標準に合わせて厳密に出力したい
Open XML SDKの注意点(質問要件に照らした弱点)
- コメント:実装は可能ですが、コメント用パーツ追加や関連付けなどが必要になり、学習コストが高くなりがちです。
- 列幅の自動調整:ExcelのAutoFit相当をSDK単体で簡単に再現するAPIは基本的にありません。幅を固定で設定するか、別途「文字幅計測」の仕組みを自前で持つ必要があります。
Open XML SDKの最小サンプル(書き込みの雰囲気)
Open XML SDKは、WorkbookPart / WorksheetPart / SheetData…のように部品を組み立てていく流れになります。ClosedXMLに比べるとコードが長くなりやすい点は織り込んでおきましょう。
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
public static class OpenXmlMinimalExport
{
public static void Export(string path)
{
using var doc = SpreadsheetDocument.Create(path, SpreadsheetDocumentType.Workbook);
var workbookPart = doc.AddWorkbookPart();
workbookPart.Workbook = new Workbook();
var worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
var sheetData = new SheetData();
// 1行目(ヘッダー)
sheetData.AppendChild(new Row(
new Cell { DataType = CellValues.String, CellValue = new CellValue("ID") },
new Cell { DataType = CellValues.String, CellValue = new CellValue("Name") },
new Cell { DataType = CellValues.String, CellValue = new CellValue("Age") }
));
// 2行目(データ例)
sheetData.AppendChild(new Row(
new Cell { DataType = CellValues.Number, CellValue = new CellValue("1") },
new Cell { DataType = CellValues.String, CellValue = new CellValue("Alice") },
new Cell { DataType = CellValues.Number, CellValue = new CellValue("29") }
));
worksheetPart.Worksheet = new Worksheet(sheetData);
var sheets = workbookPart.Workbook.AppendChild(new Sheets());
sheets.Append(new Sheet
{
Id = workbookPart.GetIdOfPart(worksheetPart),
SheetId = 1,
Name = "Data"
});
workbookPart.Workbook.Save();
}
}
用途別:無料で使える周辺候補(ClosedXMLだけで足りない時)
質問要件そのものはClosedXMLが最短ですが、現場では「相手が古い.xlsを送ってくる」「数十万行を高速に流し込みたい」など、要件が追加されがちです。そうした時の補欠も押さえておくと設計がブレません。
| ライブラリ | 強み | 弱み/注意 | ライセンス |
|---|---|---|---|
| ExcelDataReader | 読み取り特化で軽量。xlsx/xls/xlsb/csvなど幅広く読める | 基本は「読むだけ」。フォーマットは直接扱わない(必要なら別ライブラリ併用) | MIT |
| NPOI | xls/xlsxを扱える。Office不要。機能カバー範囲が広い | APIはやや独特。ClosedXMLほど直感的ではないことがある | Apache-2.0 |
| MiniExcel | ストリーミング指向で低メモリ・大規模データに強い | 「帳票の見た目」や高度なExcel機能はClosedXMLほど得意ではないことが多い | Apache-2.0 |
ExcelDataReaderはMITライセンスで、対応フォーマット一覧(.xlsx/.xlsb/.xls/.csvなど)がREADMEに明記されています。また、フォーマット(表示形式)は直接サポートしない方針も説明されています。
NPOIは「Office不要」「xls/xlsx対応」などを特徴として掲げており、古い.xlsが混ざる現場では選択肢になりやすいです。
MiniExcelはApache-2.0ライセンスで、ストリーミング(行ごと処理)による低メモリ志向を前面に出しています。ClosedXMLでメモリや速度が厳しい局面の「割り切り解」として検討余地があります。
選定で失敗しないためのチェックリスト
ファイル形式と運用ルール
- 受け取るExcelは.xlsx固定にできるか(.xls混在なら対策が必要)
- マクロ付き(.xlsm)を扱う必要があるか
- コメントは「ノート(旧コメント)」で良いか(Excelの新しいスレッドコメントは別物)
インポート側の品質要件
- ヘッダー名で列をマッピングする(列順が変わっても壊れにくい)
- 入力チェック(必須、型、範囲、桁数)をExcel任せにせず、アプリ側でも必ず行う
- 式セルを許可するか(式の結果を読む/式そのものを禁止する等の方針を決める)
数式セルの扱い(ClosedXMLの場合)
ClosedXMLは数式を扱えますが、ファイル保存時に数式結果を保持するかどうか、計算をいつ走らせるかで挙動が変わります。たとえば、値取得で計算が走ることがあるため、インポート性能や「Excelで開いた時に初めて計算される」点を意識してください。
まとめ:要件を満たしつつ「運用で揉めない」おすすめ構成
- Office不要・コメント・列幅自動調整・実装の簡単さまで含めて最初の一手はClosedXML
- Open XMLの構造まで厳密に制御したい/生成物を最適化したいならOpen XML SDK
- .xls混在や巨大データなど「追加要件」が来たら、NPOI/ExcelDataReader/MiniExcelの併用も検討
EPPlusのライセンス不安を避けつつ、商用でも無料で導入しやすいOSSライブラリに寄せるなら、まずClosedXML(必要に応じてOpen XML SDK)という選び方が、実装・運用の両面でバランスが取りやすいです。

コメント