DataTableの内容をcsv出力する(型付きでも可)

よくGridvuewなどにバインドしたデータテーブルをCSV(TSV)ダウンロードしたいということがあります。
以下そのコードです。
ポリモーフィズムを使いたかったので、オリジナルで持っていたコードとは別物になってしまいましたが・・・^^;

なお、コードの内容については責任は持てません。使用者が最終的な判断をしてください。

使い方としては

  1. ページ内で適当なDatatableを生成
  2. DownloadHandlerクラスのGetNewInstance関数でDataTableをセット 第2引数でTSVかどうかを選択
  3. IsHeadrWrite プロパティ お好みでタイトル行出力要否をセット デフォルトは出力する
  4. Encode プロパティ デフォルトでの設定 CSVはASCII TSVはUNICODE(UTF-16)BOM付き Excelでの使用を前提にしていますので、このあたりは適当に・・・
  5. DownLoad(Page page)関数 ダウンロード開始です。

○ ダウンロード出力イベント


protected void Page_Load(object sender, EventArgs e)
{
//ページ出力処理とか
}
protected void BtnCsvDownload_Click(object sender, EventArgs e)
{
bool IsTsv = false;

DataTable oDt = new DataTable("tablename");
oDt.Columns.Add(new DataColumn("column1"));
oDt.Columns.Add(new DataColumn("column2"));
oDt.Columns.Add(new DataColumn("column3"));
oDt.Columns.Add(new DataColumn("column4"));

oDt.Rows.Add("row1A", "row1b", "row1c", "row1d");
oDt.Rows.Add("row2A", "row2b", "row2c", "row2d");

DownloadHandler oHandler = DownloadHandler.GetNewInstance(oDt, IsTsv);
//oHandler.IsHeadrWrite = false;
oHandler.DownLoad(this);
}

○ ダウンロード制御クラス


public class DownloadHandler
{
/// <summary>
/// ダウンロード情報
/// </summary>
private DownloadInfo _Info;

/// <summary>
/// エンコード
/// </summary>
private Encoding _Encode;

/// <summary>
/// TSVかどうか
/// </summary>
private bool _IsTsv = false;

/// <summary>
/// タイトル行を出力するかどうか
/// </summary>
private bool _IsHeaderWrite = true;

/// <summary>
/// CSVダウンロード管理クラスの生成
/// </summary>
/// <param name="oDataset">Dataset(型付Datasetでも)</param>
/// <param name="IsTsv">true:タブ区切り(デフォルト:Unicode)falseカンマ区切り(デフォルト:ASCII)</param>
/// <returns>DownloadHandler</returns>
static public DownloadHandler GetNewInstance( DataTable oDataset, bool IsTsv)
{
DownloadHandler oHdlr = new DownloadHandler();

if (IsTsv == false)
{
oHdlr._Info = new CsvDlInfo(oDataset);
oHdlr._Encode = System.Text.Encoding.ASCII;
}
else
{
oHdlr._Info = new TsvDlInfo(oDataset);
oHdlr._Encode = System.Text.Encoding.Unicode;
}

oHdlr._IsTsv = IsTsv;

return ( oHdlr );
}

/// <summary>
/// エンコードプロパティ
/// </summary>
public Encoding Encode
{
get { return (_Encode); }
set { _Encode = value; }
}

private DownloadInfo GetDownloadInfo()
{
return this._Info;
}

/// <summary>
/// タイトル行出力要否プロパティ
/// </summary>
public bool IsHeadrWrite
{
get { return (_IsHeaderWrite); }
set { _IsHeaderWrite = value; }
}

/// <summary>
/// エラー出力処理
/// </summary>
private void error(Page oPage)
{
oPage.Response.ContentType = "application/octet-stream";
oPage.Response.AddHeader("Content-Disposition", "attachment;filename=error.csv");
oPage.Response.Write("SYSTEM_ERRORrn");
oPage.Response.End();
}

/// <summary>
/// ページのロード処理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void DownLoad( Page oPage )
{
DownloadInfo oInfo =  GetDownloadInfo();

// ファイルの名前を設定
String fname = oInfo.GetFileName();

// ダウンロード時のファイル名設定
oPage.Response.AddHeader("Content-Disposition", "attachment;filename=" + fname);

// ページのHeaderに情報をセット
oPage.Response.ContentType = "application/octet-stream";

// エンコードの設定
Encoding oEncode = this.Encode;
if (oEncode != null)
{
oPage.Response.Charset = oEncode.BodyName;
oPage.Response.ContentEncoding = oEncode;

//エンコードがUTF-16(Unicode)の場合、Bom付きで出力する。
if (oEncode == Encoding.Unicode && _IsTsv == true)
{
//Bomを付ける:ExcelでUnicode『.csv』はBom無しは開けない仕様のため。
byte[] oBom = Encoding.Unicode.GetPreamble();

//Bomバイトコードの出力
for (int nIndex = 0; nIndex < oBom.Length; nIndex++)
{
oPage.Response.OutputStream.WriteByte(oBom[nIndex]);
}
}
}

///出力
oInfo.StreamWrite(oPage.Response.Output);

///終了
oPage.Response.End();

}

}

○ ダウンロード情報管理基底クラス


public abstract class DownloadInfo
{
/// <summary>
/// データテーブル
/// </summary>
protected DataTable _DataTable;

/// <summary>
/// ヘッダー出力
/// </summary>
protected bool _IsHeaderWrite = true;

/// <summary>
/// ヘッダー出力プロパティ
/// </summary>
public bool IsHeaderWrite
{
get { return (_IsHeaderWrite ); }
set { _IsHeaderWrite = value;	}
}

public DownloadInfo() { }

#region 仮想メソッド
public virtual String GetFileName()
{
return null;
}

public virtual void StreamWrite(TextWriter oWriter) { }

#endregion 仮想メソッド
}

○ CSV ダウンロード情報管理クラス


/// <summary>
/// CSVデータオブジェクトクラス
/// </summary>
class CsvDlInfo : DownloadInfo
{
private readonly char[] CSVCONVERTCHAR = new char[] {',','r','n','"'};

public CsvDlInfo(DataTable oDataTable)
{
_DataTable = oDataTable;
}

/// <summary>
/// ファイル名を取得
/// </summary>
/// <returns>ファイル名</returns>
override public string GetFileName()
{
return ( String.Format("CSV{0:yyMMdd-HHmmss}.csv", DateTime.Now ) );

}

/// <summary>
/// 指定のライターへのCSVダウンロードデータ出力
/// </summary>
/// <param name="oWriter">出力先ライター</param>
override public void StreamWrite( TextWriter oWriter )
{
if (_DataTable == null)
{
oWriter.Write( "CSVDLINFO ERRORrn" );
return;
}

///
/// タイトル行
///
if (base._IsHeaderWrite == true)
{
WriteTitle(oWriter);
}

///
/// データ本体
///
WriteData(TextWriter oWriter)
oWriter.Write( "DataEndrn" );
}

/// <summary>
/// タイトル行出力
/// </summary>
/// <param name="oWriter">出力先ライター</param>
private void WriteTitle(TextWriter oWriter)
{
// タイトル行
for (int col = 0; col < _DataTable.Columns.Count; col++)
{
oWriter.Write(col == 0 ? String.Empty : ",");

DataColumn dc = _DataTable.Columns[col];
oWriter.Write(this.GetCSVOutString(dc.ColumnName) );
}
oWriter.Write("rn");
}

/// <summary>
/// データ行出力
/// </summary>
/// <param name="oWriter">出力先ライター</param>
private void WriteData(TextWriter oWriter)
{
for (int row = 0; row < _DataTable.Rows.Count; row++)
{
DataRow dr = _DataTable.Rows[row];
for (int col = 0; col < _DataTable.Columns.Count; col++)
{

//データ出力
oWriter.Write( col == 0 ? String.Empty : ",");

string szStr = dr[col].ToString();
oWriter.Write( this.GetCSVOutString( szStr ) );

}
oWriter.Write( "rn" );
}
}

/// <summary>
/// CSV(カンマ区切り)出力するための文字列変換
/// 例)	文字中に『,』がある場合『"』で文字列をくくる
///		Test,String -> "Test,String"
///		文字前後に『"』がある場合『""』で文字列をくくる
///		"Test,String" -> """Test,String"""
/// </summary>
/// <param name="szStr">変換前の文字列</param>
/// <returns>変換後の文字列</returns>
private string GetCSVOutString(String szStr)
{
if ((szStr == null) || (szStr.Length <= 0)) return (String.Empty);

///	文字の前後にダブルクオートを付与する必要かあるどうか?
if (szStr.IndexOfAny(CSVCONVERTCHAR) < 0) return (szStr);

///文字中にダブルクオートがある場合
int nIndex = 0; string szDoubleQuote = """;
while ((nIndex = szStr.IndexOf(szDoubleQuote, nIndex)) >= 0)
{
szStr = szStr.Insert(nIndex, szDoubleQuote);
//追加文字列 + 1字後から再度検索する
nIndex += szDoubleQuote.Length + 1;
}

return (String.Format(""{0}"", szStr));

}
}

○ TSV ダウンロード情報管理クラス


/// <summary>
/// TSVデータオブジェクトクラス
/// </summary>
class TsvDlInfo : DownloadInfo
{
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="oDataTable">DataTable</param>
public TsvDlInfo( DataTable oDataTable )
{
_DataTable = oDataTable;
}

/// <summary>
/// ファイル名を取得
/// </summary>
/// <returns>ファイル名</returns>
override public String GetFileName()
{
return (String.Format("TSV{0:yyMMdd-HHmmss}.csv", DateTime.Now));
}

/// <summary>
/// 指定のライターへのTSVダウンロードデータ出力
/// </summary>
/// <param name="tw">出力先ライター</param>
override public void StreamWrite(TextWriter oWriter)
{
if (_DataTable == null)
{
oWriter.Write( "TSVDLINFO ERRORrn" );
return;
}

///
/// タイトル行
///
if ( base._IsHeaderWrite == true )
{
WriteTitle(oWriter);
}

///
/// データ本体
///
WriteData(oWriter);

oWriter.Write( "DataEndrn" );
}

/// <summary>
/// タイトル行出力
/// </summary>
/// <param name="oWriter">出力先ライター</param>
private void WriteTitle( TextWriter oWriter )
{
int nColumnCount = _DataTable.Columns.Count;
for (int nClIndex = 0; nClIndex < nColumnCount; nClIndex++)
{
oWriter.Write(nClIndex == 0 ? String.Empty : "t");

//タイトル出力
DataColumn oDataColumn = _DataTable.Columns[nClIndex];
oWriter.Write(String.Empty + oDataColumn.ColumnName);
}
//改行
oWriter.Write("rn");
}

/// <summary>
/// データ行出力
/// </summary>
/// <param name="oWriter">出力先ライター</param>
private void WriteData(TextWriter oWriter)
{
for (int row = 0; row < _DataTable.Rows.Count; row++)
{
DataRow dr = _DataTable.Rows[row];
for (int col = 0; col < _DataTable.Columns.Count; col++)
{

//データ出力
oWriter.Write( col == 0 ? String.Empty : "t");

string szStr = dr[col].ToString();
oWriter.Write(  szStr  );

}
oWriter.Write( "rn" );
}
}

}