DataTableの内容をcsv出力する(型付きでも可)
よくGridvuewなどにバインドしたデータテーブルをCSV(TSV)ダウンロードしたいということがあります。
以下そのコードです。
ポリモーフィズムを使いたかったので、オリジナルで持っていたコードとは別物になってしまいましたが・・・^^;
なお、コードの内容については責任は持てません。使用者が最終的な判断をしてください。
使い方としては
- ページ内で適当なDatatableを生成
- DownloadHandlerクラスのGetNewInstance関数でDataTableをセット 第2引数でTSVかどうかを選択
- IsHeadrWrite プロパティ お好みでタイトル行出力要否をセット デフォルトは出力する
- Encode プロパティ デフォルトでの設定 CSVはASCII TSVはUNICODE(UTF-16)BOM付き Excelでの使用を前提にしていますので、このあたりは適当に・・・
- 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" );
}
}
}