ASP.NET MVC Export CSV (StringBuilder & DataTables)
2022-02-08
說明 ASP.NET MVC 如何將各式資料來源匯出為 CSV 格式資料,比較 StringBuilder
建立字串的原生 C# 解決方案以及使用 DataTables 的 Zero Dev 解決方案 🙂
StringBuilder
匯出為 CSV 使用的是 StringBuilder
組合字串的方式。
首先是從 DBContext 取得相關的資料,可以搭配 Select 的方式篩選特定的資料欄位。
var data = db.Customer
.Select(i => new
{
i.FirstName,
i.LastName,
i.CompanyName
})
.ToList();
接著透過 StringBuilder
來建構 CSV 內容,藉由 Reflection
的方式可以迭代所有的 Row 匿名類型的屬性,免去逐一輸入的困擾。
using System.Reflection;
using System.Text;
StringBuilder sb = new StringBuilder();
sb.AppendLine("FirstName,LastName,CompanyName");
foreach (var row in data)
{
foreach (PropertyInfo prop in row.GetType().GetProperties())
{
var propertyValue = prop.GetValue(row, null);
var cellValue =
propertyValue == null ?
"," : "\"" + prop.GetValue(row, null).ToString() + "\"" + ',';
sb.Append(cellValue);
}
sb.Append("\r\n");
}
使用 File 的方式回傳 ActionResult,其中 StringBuilder
的內容為 Bytes 必須藉由 Encoding.GetBytes
來進行編碼。
這邊選擇 Default
的編碼方式,會預設回應 ANSI 編碼的 CSV,如果希望回應 UTF-8 編碼的 CSV 可以在另行調整。但 Excel 預設會以 ANSI 的方式解碼,會先看到亂碼必須要再自行調整編碼方式,稍微不方便。
return File(
Encoding.Default.GetBytes(
sb.ToString()),
"text/csv",
string.Format("Export-{0}.csv", DateTime.Now.ToString("yyyyMMdd-HHmmss")
));
DataTables.js
使用 DataTables 的方式僅需要藉由 JS Object 來設定 Library 使用就可以輕鬆進行資料的匯出,除了 CSV 外,Excel、PDF 或者是 Print 的方式都支援,同時提供使用者自行篩選資料的功能,能夠減少許多繁瑣的開發工作。
需要安裝的 Library,使用 cdnjs 搭配 libman.json 進行管理。
libman.json
{
"version": "1.0",
"defaultProvider": "cdnjs",
"libraries": [
{
"library": "[email protected]",
"destination": "lib/datatables.net/"
},
{
"library": "[email protected]",
"destination": "lib/datatables.net-bs/"
},
{
"library": "[email protected]",
"destination": "lib/jszip/"
},
{
"library": "[email protected]",
"destination": "lib/pdfmake/"
},
{
"library": "[email protected]",
"destination": "lib/datatables-buttons/"
},
{
"library": "[email protected]",
"destination": "lib/datatables.net-buttons-bs/"
},
{
"library": "[email protected]",
"destination": "lib/datatables.net-buttons-dt",
"files": [
"buttons.dataTables.css",
"buttons.dataTables.min.css"
]
}
]
}
view.cshtml / style sections
<link href="~/lib/datatables.net-bs/dataTables.bootstrap.css" rel="stylesheet" />
<link href="~/lib/datatables.net-buttons-dt/buttons.dataTables.min.css" rel="stylesheet" />
<style>
.dataTables_info {
float: left;
}
table.dataTable {
margin-top: 2rem !important;
margin-bottom: 2rem !important;
}
</style>
view.cshtml
<h2>DataTables</h2>
<table class="table table-bordered" id="dataTable">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.FirstName)
</th>
...
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.FirstName)
</td>
...
</tr>
}
</tbody>
</table>
view.cshtml / section scripts
@section scripts {
...
}
下列的 Script 都是放在 section scripts
當中:
@* DataTables Cores *@
<script src="~/lib/datatables.net/jquery.dataTables.min.js"></script>
<script src="~/lib/datatables.net-bs/dataTables.bootstrap.min.js"></script>
@* DataTables Buttons *@
<script src="~/lib/datatables-buttons/js/dataTables.buttons.min.js"></script>
<script src="~/lib/datatables.net-buttons-bs/buttons.bootstrap.min.js"></script>
@* Basic *@
<script src="~/lib/datatables-buttons/js/buttons.html5.min.js"></script>
@* Excel *@
<script src="~/lib/jszip/jszip.js"></script>
@* Print *@
<script src="~/lib/datatables-buttons/js/buttons.print.min.js"></script>
@* PDF *@
<script src="~/lib/pdfmake/pdfmake.min.js"></script>
<script src="~/lib/pdfmake/vfs_fonts.min.js"></script>
關於 DataTables
Init 上實際的呼叫函式。
$(document).ready(function () {
$('#dataTable').DataTable({
"searching": true,
"pageLength": 50,
"bLengthChange": false,
"order": [[0, "desc"]], // Default Order
"columnDefs": [
{ "targets": 0, "orderable": true },
{ "targets": 1, "orderable": false },
{ "targets": 1, "searchable": false },
],
"language": {
"lengthMenu": "Display _MENU_ records per page",
"zeroRecords": "沒有任何查詢結果,請調整搜尋條件後再執行。",
"info": "搜尋共有 _TOTAL_ 個項目",
"infoEmpty": "",
"infoFiltered": "(filtered from _MAX_ total records)",
},
"dom": "Blfrtip",
"buttons": [
'copy',
'excel',
'csv',
'pdf',
'print'
]
});
});
需要注意的 dom
一定要加入在 DataTables 的啟動函式當中,否則 Buttons 不會正常顯示。
關於 DataTables 的 dom
可以參考 dom,關於 DataTables buttons
可以參考 buttons
待解 Bugs
使用 PDF 匯出的文件中文標題無法正常顯示 😥