Export ข้อมูลไฟล์ Excel ในแบบ Single และ Multiple sheet ด้วย ASP.NET(C#)

            ความเดิมตอนที่แล้ว ผู้เขียนได้พูดถึงความสำคัญของข้อมูล และวิธีการ Import ข้อมูลจากไฟล์ Excel กันไปพอสมควร สำหรับในบทความนี้ ผู้เขียนจะขอพูดถึงวิธีการส่งออกข้อมูล หรือที่เรามักเรียกกันติดปากว่า “Export ข้อมูล” กันบ้าง เพื่อให้ผู้พัฒนาที่มีความสนใจสามารถพัฒนาโปรแกรมได้ครบวงจรทั้งแบบนำเข้าและส่งออกข้อมูล โดยผู้เขียนจะไม่ขอพูดถึงในรายละเอียดที่ได้กล่าวไว้แล้วก่อนหน้า ผู้อ่านสามารถอ่านรายละเอียดเพิ่มเติมได้ในบทความ “เรียนรู้วิธีการ Import ข้อมูลในรูปแบบไฟล์ Excel ด้วย ASP.NET (C#)” สำหรับในบทความนี้ผู้เขียนจะเน้นในส่วนของการ Export ข้อมูลเท่านั้น ซึ่งจะมีการอธิบายใน 2 ลักษณะเช่นกัน คือ แบบ Single sheet และแบบ Multiple sheet เพื่อให้เห็นเป็นแนวทางและสามารถนำไปต่อยอดการพัฒนาเพิ่มเติมสำหรับแต่ละท่านได้

กรณีส่งออกข้อมูล(Export) ซึ่งในบทความนี้จะแบ่งเป็น 2 ลักษณะ ดังนี้

  • การส่งออกข้อมูลแบบ Single-sheet ซึ่งมีขั้นตอนการทำงานไม่ซับซ้อนมากนัก ดังนี้
protected void btnExport_Click(object sender, EventArgs e)

{
//// ตารางสมมติ สร้างขึ้นเพื่อให้ผู้พัฒนาเห็นภาพ หากเป็นกรณีใช้งานจริงจะเป็นข้อมูลที่ดึงจากฐานข้อมูลเพื่อ Export ในรูปแบบไฟล์ Excel
DataTable table = new DataTable();
table.Columns.Add("Name", typeof(string));
table.Columns.Add("Latitude", typeof(decimal));
table.Columns.Add("Longitude", typeof(decimal));
table.Columns.Add("Description", typeof(string));
table.Rows.Add("University1", 7.006923, 100.500238, "Desc1");
table.Rows.Add("University2", 7.172661, 100.613726, "Desc2");



StringBuilder sb = new StringBuilder();
if (table.Rows.Count > 0)
{
string fileName = Path.Combine(Server.MapPath("~/ImportDocument"), DateTime.Now.ToString("ddMMyyyyhhmmss") + ".xls");

//// ลักษณะการตั้งค่าเพื่อเชื่อมต่อด้วย OleDb ซึ่งในกรณีนี้ไฟล์ Excel จะต้องมีนามสกุลเป็น .xls แต่หากเป็นนามสกุลแบบ .xlsx ต้องเปลี่ยนการกำหนดค่าให้เป็น
conString = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + 
fileName + ";Extended Properties='Excel 12.0;HDR=YES;IMEX=1;';";  แทน

string conString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + fileName + ";Extended Properties=\"Excel 8.0;HDR=Yes;IMEX=2\"";


 using (OleDbConnection con = new OleDbConnection(conString))
{
////เขียนคำสั่งในการสร้างตาราง ซึ่งในที่นี้คือ WorkSheet ที่ต้องการ พร้อมทั้งกำหนดชื่อและชนิดของข้อมูลในแต่ละคอลัมน์
string strCreateTab = "Create table University (" +
" [Name] varchar(50), " +
" [Latitude] double, " +
" [Longitude] double, " +
" [Description] varchar(200)) ";



if (con.State == ConnectionState.Closed)
{
con.Open();
}
////รันคำสั่งที่เขียนในการสร้างตาราง
OleDbCommand cmd = new OleDbCommand(strCreateTab, con);
cmd.ExecuteNonQuery();

////เขียนคำสั่งในการเพิ่มข้อมูล(insert) ข้อมูลในแต่ละฟิลด์ รวมทั้งประกาศพารามิเตอร์ที่ใช้ในการรับค่าข้อมูลที่อ่านได้
string strInsert = "Insert into University([Name],[Latitude]," +
" [Longitude], [Description]" +
") values(?,?,?,?)";

OleDbCommand cmdIns = new OleDbCommand(strInsert, con);
cmdIns.Parameters.Add("?", OleDbType.VarChar, 50);
cmdIns.Parameters.Add("?", OleDbType.Double);
cmdIns.Parameters.Add("?", OleDbType.Double);
cmdIns.Parameters.Add("?", OleDbType.VarChar, 200);

////วนค่าที่ได้จากฐานข้อมูลและกำหนดค่าให้กับพารามิเตอร์และรันคำสั่งในการเพิ่มข้อมูลทีละรายการ
foreach (DataRow  i in table.Rows)
{
cmdIns.Parameters[0].Value = i["Name"];
cmdIns.Parameters[1].Value = i["Latitude"];
cmdIns.Parameters[2].Value = i["Longitude"];
cmdIns.Parameters[3].Value = i["Description"];
cmdIns.ExecuteNonQuery();

}
}

////สร้างไฟล์ที่ต้องการดาวน์โหลดจากการอ่านที่ได้ทั้งหมด เพื่อบันทึกเป็นไฟล์ Excel ที่มีชื่อว่า University.xls

byte[] content = File.ReadAllBytes(fileName);
HttpContext context = HttpContext.Current;
context.Response.BinaryWrite(content);
context.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
context.Response.AppendHeader("Content-Disposition", "attachment; filename=University.xls");
Context.Response.End();
}
}
  • การส่งออกข้อมูลแบบ Multiple sheet ใช้ในกรณีที่มีข้อมูลที่ Export จำนวนมากและต้องการแบ่งข้อมูลที่มีอยู่ออกเป็น WorkSheet ย่อย ซึ่งหลักการทำงานจะคล้ายกับการส่งออกข้อมูลแบบ Single-sheet แต่จะซับซ้อนกว่าในส่วนของการวนค่าจากในฐานข้อมูลมาใส่ใน  WorkSheet โดยมีการกำหนดจำนวนแถวที่จะให้บันทึกในแต่ละ WorkSheet หากเกินค่าที่กำหนดจะสร้าง WorkSheet ใหม่และบันทึกข้อมูลลงไปใน WorkSheet นั้น
static StringBuilder sqlInsert = new StringBuilder();
static StringBuilder strScript = new StringBuilder();

public static void ExportToMultipleXLSheets( System.String OutputFileName)
{
string connstr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" +
mOutputFileName + ";Extended Properties='Excel 8.0'";
OleDbConnection xlConn = new OleDbConnection(connstr);

try
{
xlConn.Open();
////ตารางสมมติเช่นเดียวกับที่ได้กล่าวไว้ในแบบการส่งออกข้อมูลแบบ Single sheet
DataTable table = new DataTable();
table.Columns.Add("EmployeeID", typeof(string));
table.Columns.Add("Name", typeof(string));
table.Columns.Add("LastName", typeof(string));
table.Columns.Add("Position", typeof(string));

table.Rows.Add("0001", "Jack", "Rayman", "Programmer");
table.Rows.Add("0002", "John", "Wicky", "Programmer");
table.Rows.Add("0003", "Jenifer", "Wincy", "Programmer");
table.Rows.Add("0004", "Kate", "Wrapper", "Manager");
table.Rows.Add("0005", "Bella", "Cole", "Programmer");
table.Rows.Add("0006", "Sandy", "Stick", "Programmer");
table.Rows.Add("0007", "Tom", "Runner", "Programmer");

////เป็นการเรียกใช้เมธอดที่ใช้ในการเตรียมคำสั่งที่จะใช้วนสร้างตาราง/Worksheet
PrepareScript(table);

////เป็นเมธอดที่ใช้ในการเริ่มต้นทำงานคำสั่งในการ Export โดยจะมีการคำนวณจำนวนแถวของข้อมูลว่าเกินที่ระบุไว้หรือไม่ หากเกินกำหนดจะสร้าง WorkSheet ใหม่นั่นเอง
StartExport(table, xlConn);

if (xlConn != null)
{
if (xlConn.State == ConnectionState.Open) xlConn.Close();
xlConn.Dispose();
}

////อ่านค่าและ Export ไปยังไฟล์ที่มีชื่อว่า ExportMultiSheetData.xls
byte[] content = File.ReadAllBytes(mOutputFileName);
HttpContext context = HttpContext.Current;
context.Response.BinaryWrite(content);
context.Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
context.Response.AppendHeader("Content-Disposition", "attachment; filename=ExportMultiSheetData.xls"  );
context.Response.End();

}
catch (Exception exp)
{
throw new Exception("ImportToMultipleXLSheets", exp.InnerException);
}
finally
{
if (xlConn != null)
{
if (xlConn.State == ConnectionState.Open) xlConn.Close();
xlConn.Dispose();
}
}
}
o เมธอดในการเตรียมคำสั่งที่ใช้ในการสร้างตารางและเพิ่มข้อมูล
private static string PrepareScript(DataTable DTable)
{
// เตรียมคำสั่งในการสร้าง WorkSheet
sqlInsert.Length = 0;
strScript.Length = 0;

////วนค่าเพื่ออ่านค่าคอลัมน์ที่มีในแต่ละตาราง
for (int i = 0; i < DTable.Columns.Count; i++)
{
sqlInsert.Append( "[" + DTable.Columns[i].ColumnName + "],");
strScript.Append("[" + DTable.Columns[i].ColumnName.Replace("'", "''") + "]");

////กำหนดชนิดของข้อมูลแต่ละคอลัมน์
if (DTable.Columns[i].DataType.ToString().ToLower().Contains("int") || DTable.Columns[i].DataType.ToString().ToLower().Contains("decimal"))
strScript.Append(" double");
else
strScript.Append(" text");
strScript.Append(", ");
}

sqlInsert.Remove(sqlInsert.Length - 1, 1);
strScript.Remove(strScript.Length - 2, 1);
strScript.Append(") ");
////ผลที่ได้ : 
sqlInsert >> [EmployeeID],[Name],[LastName],[Position]
strScript >> [EmployeeID] text, [Name] text, [LastName] text, [Position] text ) 
เพื่อนำไปใช้ในการ run คำสั่งการทำงานสร้าง (create table) และเพิ่มข้อมูล (insert)

return strScript.ToString();
}
o เมธอดในการสร้างตาราง/WorkSheet ตามคำสั่งที่เตรียมไว้
private static void CreateXLSheets(DataTable DTable,
OleDbConnection xlConn, System.String XLSheetName)
{
// สร้าง WorkSheet ใหม่
System.Text.StringBuilder SqlFinalScript = new System.Text.StringBuilder();

OleDbCommand cmdXl = new OleDbCommand();
try
{
SqlFinalScript.Length = 0;
cmdXl.Connection = xlConn;

///สร้างคำสั่งในการสร้างตาราง/WorkSheet ขึ้นใหม่ โดยตั้งชื่อตามพารามิเตอร์ที่ได้รับมา
SqlFinalScript.Append("CREATE TABLE " + XLSheetName + " (");
SqlFinalScript.Append(strScript.ToString());
cmdXl.CommandText = SqlFinalScript.ToString();
cmdXl.ExecuteNonQuery();
}
catch (Exception xlSheetExp)
{
throw (new Exception("CreateXLSheetException", xlSheetExp.InnerException));
}
finally
{
cmdXl.Dispose();
}
}
private static void StartExport(DataTable DTable, OleDbConnection xlConn)
{
Int64 rowNo = 0, xlSheetIndex = 1, TotalNoOfRecords = 0;
System.String NewXLSheetName = "Sheet";
System.Text.StringBuilder strInsert = new System.Text.StringBuilder();
TotalNoOfRecords = DTable.Rows.Count;
OleDbCommand cmdXl = new OleDbCommand();
cmdXl.Connection = xlConn;
for (int count = 0; count < DTable.Rows.Count; count++)
{
strInsert.Length = 0;
////กรณีที่เป็นแถวแรกจะทำการสร้าง WorkSheet ขึ้นใหม่ โดยเรียกใช้เมธอด CreateXLSheets
if (rowNo == 0 )
{ CreateXLSheets(DTable, xlConn, NewXLSheetName + xlSheetIndex);
}
rowNo += 1;

////กรณีที่จำนวนแถวใน 1 WorkSheet เกินที่กำหนดไว้จะสร้างชีทใหม่ ในกรณีนี้คือจะสร้าง WorkSheet ใหม่ทุกๆ 3 แถว และเพิ่มลำดับของ index ของ WorkSheet คราวละ 1
if (TotalNoOfRecords > 3 && rowNo > 3
{
xlSheetIndex += 1;
CreateXLSheets(DTable, xlConn, NewXLSheetName + xlSheetIndex);
rowNo = 1;
}

////เขียนคำสั่งในการเพิ่มข้อมูลไปยัง WorkSheet ที่กำหนด โดยมีการแทนที่ค่าของคำสั่งที่เตรียมไว้แล้วก่อนหน้านี้ในตอนเรียกใช้เมธอด PrepareScript
strInsert.Append("Insert Into [" + NewXLSheetName + xlSheetIndex.ToString() +  "$](" + sqlInsert.ToString() + ") Values (");


foreach (DataColumn dCol in DTable.Columns)
{
if (dCol.DataType.ToString().ToLower().Contains("int"))
{
if (DTable.Rows[count][dCol.Ordinal].ToString() == "")
strInsert.Append("NULL");
else
strInsert.Append(DTable.Rows[count][dCol.Ordinal]);
}
else if (dCol.DataType.ToString().ToLower().ToLower().Contains("decimal"))
{
if (DTable.Rows[count][dCol.Ordinal].ToString() == "")
strInsert.Append("NULL");
else
strInsert.Append(DTable.Rows[count][dCol.Ordinal]);
}
else
strInsert.Append("\"" +
DTable.Rows[count][dCol.Ordinal].ToString().Replace("'",
"''") + "\"");
strInsert.Append(",");
}
strInsert.Remove(strInsert.Length - 1, 1);
strInsert.Append(");");

////run คำสั่งที่สร้างขึ้นในการเพิ่มข้อมูลทีละรายการ
cmdXl.CommandText = strInsert.ToString();
cmdXl.ExecuteNonQuery();

}
}
o เขียนการทำงานเมื่อกดปุ่ม “Export”
protected void btnExport_Click(object sender, EventArgs e)
{ 
 string fileName = Path.Combine(Server.MapPath("~/ImportDocument"), Guid.NewGuid().ToString() + ".xls");
//เรียกใช้เมธอดในการ Export ข้อมูลแบบ Multiple sheet
ExportToMultipleXLSheets(fileName);

}

หลักการทำงานคร่าวๆในการ Export ข้อมูลใน 2 ลักษณะจะมีหลักการพื้นฐานคล้ายกัน ซึ่งจะสรุปได้ดังนี้

  • ดึงข้อมูลจากฐานข้อมูลมาเก็บไว้ใน Datadatable หรือ Dataset
  • กำหนดชื่อไฟล์ที่จะใช้ในการเชื่อมต่อกับ OleDb
  • เชื่อมต่อกับ OleDb โดยการกำหนดค่าต่างๆ เพื่อใช้ในการทำงานกับไฟล์ Excel นั้นๆ
  • เตรียมกำหนดคำสั่ง sql command ในการสร้างโครงสร้างตาราง(create table) และ เพิ่มข้อมูล (insert) ในตารางที่สร้างไว้ หากเป็นกรณีที่ต้องการสร้างแบบ Multiple sheet
    ก็จะทำการเตรียมชุดคำสั่งไว้ และนำไปวน run คำสั่งนั้นๆตามจำนวนรอบที่มีการสร้าง WorkSheet ใหม่นั่นเอง
  • สั่ง run คำสั่งดังกล่าว หากจำนวนแถวของข้อมูลเกินกว่าที่กำหนดต่อ 1 WorkSheet (ซึ่งในกรณีสมมติให้เป็น 3 แถว) จะทำการสร้าง WorkSheet ใหม่
  • อ่านค่าที่ได้จากไฟล์ที่เชื่อมต่อกับ OleDb และผ่านกระบวนการ Export ข้อมูล  มาเขียนเป็นไฟล์ Excel เพื่อให้ผู้ใช้สามารถบันทึกข้อมูลในรูปแบบไฟล์ที่ Export ได้
  • หากเป็นการ Export ข้อมูลแบบ Multiple sheet จำนวนของ WorkSheet จะขึ้นอยู่กับข้อมูลที่ดึงมาและจำนวนที่กำหนดไว้ต่อ 1 WorkSheet นั่นเอง
หมายเหตุ : Namespace ที่ต้องอ้างอิงเพิ่มเติมในการใช้งานโค้ดที่กล่าวไว้ข้างต้น มีดังนี้
            –  System.IO
            –  System.Data.OleDb
            –  System.Data
            –  System.Text

            บทความนี้ถือเป็นเพียงการแนะนำวิธีเบื้องต้นในการ Export ข้อมูลไฟล์ในรูปแบบ Excel เท่านั้น ซึ่งผู้พัฒนาสามารถนำความรู้หรือข้อมูลที่ได้จากบทความนี้ไปประยุกต์ ดัดแปลงหรือเพิ่มลูกเล่นให้กับงานที่ท่านกำลังพัฒนาอยู่ได้ เพื่อเพิ่มความสามารถในกระบวนการนำ-เข้าส่งออกดังกล่าว นอกจากนี้ผู้เขียนหวังเป็นอย่างยิ่งว่าบทความนี้อาจเป็นอีกหนึ่งทางเลือกสำหรับผู้พัฒนามือใหม่หรือผู้ที่กำลังค้นหาวิธีการนี้อยู่ สามารถนำไปพัฒนาต่อได้โดยง่าย หากผิดพลาดประการใด ผู้เขียนขออภัยไว้ ณ ที่นี้ค่ะ

แหล่งข้อมูลอ้างอิง :
http://www.codeproject.com/Articles/33271/Import-and-Export-to-Multiple-Worksheets
http://dotnetawesome.blogspot.com/2013/11/how-to-import-export-database-data-from_18.html