版权声明:欢迎评论和转载,转载请注明来源。 https://blog.csdn.net/zy332719794/article/details/8938475
今天接到一个任务,把数据库里面的数据导出到Excel文档。系统是Web程序的,使用的是MVC架构。第一个想法是很简单,保存.csv文件。做法是首先把数据读到一个DataTable里面,然后将数据分隔写入文件就OK了,最后在相应请求下载文件。
代码如下:
/// <summary>
/// 导出数据
/// </summary>
public ActionResult ExportData(int processId)
{
var myProcessId = processId;
var items = db.NecessaryMaterialsDbSet.Include(p => p.ProcessItem)
.OrderBy(p => p.Category)
.ToList()
.Where(p => p.ProcessItem.Id == processId);
if (!items.Any())
{
return RedirectToAction("Index", new { processId = myProcessId });
}
var dt = new DataTable();
dt.Columns.Add("类别");
dt.Columns.Add("材料名称");
dt.Columns.Add("材料来源");
dt.Columns.Add("来源类型");
dt.Columns.Add("原件、复印件");
dt.Columns.Add("备注");
foreach (var materialse in items)
{
dt.Rows.Add(materialse.Category,
materialse.Name,
materialse.Source,
materialse.SourceType,
materialse.OriginalOrCopy,
materialse.Note);
}
//生成文件
string fileName = items.First().ProcessItem.ProcessName + "-必备材料.csv";
var sw = new StringWriter();
var title = new StringBuilder();
title.Append(dt.Columns[0].ColumnName);
for (int i = 1; i < dt.Columns.Count; i++)
{
title.AppendFormat(",{0}", dt.Columns[i].ColumnName);
}
sw.WriteLine(title);
var content = new StringBuilder();
foreach (DataRow row in dt.Rows)
{
content.Append(row[0]);
for (int i = 1; i < dt.Columns.Count; i++)
{
content.AppendFormat(",{0}", row[i]);
}
sw.WriteLine(content);
content.Clear();
}
sw.Close();
Response.AddHeader("Content-Disposition", "attachment; filename=" + Server.UrlEncode(fileName));
Response.ContentType = "vnd.ms-excel.numberformat:yyyy-MM-dd ";
Response.ContentEncoding = System.Text.Encoding.GetEncoding("GB2312");
Response.Write(sw);
Response.End();
return RedirectToAction("Index", new { processId = myProcessId });
}
但是有个问题,我需要导出有合并单元格的样式的Excel,这样似乎不能实现。
如下图所示:
第一个想法是使用Office的Excel的API,有个同事建议使用Table来构造。结果试了下,果然不错。
构造方法如在html里面构造一样。使用<table><tr><tb>标签来控制行和列以及内容。
同时还可以在table里面设置属性,如<table border='1'>、<td width='100'><br>等等。
其中跨行就用rowspan属性来设置,如:"<td rowspan=5>表示跨5行(合并单元格)。
原来还有这样好的方法,于是按照这种方式写了代码,调试生成,效果非常好,上面的图就是用table的方法生成的。
下面我贴上一些实例代码:
ExpMaterialsToExcelModel.cs(这个是用来存将导出的数据类结构)
public class ExpMaterialsToExcelModel
{
/// <summary>
/// 流程名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 审批事项
/// </summary>
public List<Category> Categorys { get; set; }
public ExpMaterialsToExcelModel()
{
Categorys = new List<Category>();
}
}
public class Category
{
/// <summary>
/// 审批事项名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 承办科室
/// </summary>
public string Department { get; set; }
/// <summary>
/// 联系人及电话
/// </summary>
public string PersonAndPhone { get; set; }
/// <summary>
/// 材料
/// </summary>
public List<MaterialsInfo> MaterialsList { get; set; }
public Category()
{
MaterialsList = new List<MaterialsInfo>();
}
}
public class MaterialsInfo
{
/// <summary>
/// 排号顺序
/// </summary>
public int SortNumb { get; set; }
/// <summary>
/// 材料名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 来源类型
/// </summary>
public string SourceType { get; set; }
/// <summary>
/// 来源
/// </summary>
public string Source { get; set; }
/// <summary>
/// 备注
/// </summary>
public string Note { get; set; }
}
ExpMaterialsToExcelModel.cs(从数据库读取数据)
private ExpMaterialsToExcelModel GetExpData(int processId)
{
代码略。。。
}
GetExpString(生成导出的table文本)重点
private StringBuilder GetExpString(ExpMaterialsToExcelModel tableData)
{
int rowCount = tableData.Categorys.Sum(category => category.MaterialsList.Count);
var str = new StringBuilder();
str.Append("<meta http-equiv=\"content-type\" content=\"application/excel;charset=utf-8\" />");
str.Append("<table border='1'>");
str.Append("<tr>");
str.AppendFormat("<td width='100' rowspan='{0}'>{1}</td>", rowCount, tableData.Name);
for (int i = 0; i < tableData.Categorys.Count; i++)
{
if (i > 0)
{
str.Append("<tr>");
}
var category = tableData.Categorys[i];
rowCount = category.MaterialsList.Count;
str.AppendFormat("<td width='100' rowspan='{0}'>{1}</td>", rowCount, category.Name);
str.AppendFormat("<td width='100'>{0}</td>", category.MaterialsList[0].SourceType);
str.AppendFormat("<td width='200'>{0}.{1}</td>", category.MaterialsList[0].SortNumb,
category.MaterialsList[0].Name);
str.AppendFormat("<td width='100'>{0}</td>", category.MaterialsList[0].Note);
str.AppendFormat("<td width='100' rowspan='{0}'>{1}</td>", rowCount, category.Department);
str.AppendFormat("<td width='100' rowspan='{0}'>{1}</td>", rowCount, category.PersonAndPhone);
str.Append("</tr>");
for (int j = 1; j < category.MaterialsList.Count; j++)
{
str.Append("<tr>");
str.AppendFormat("<td width='100'>{0}</td>", category.MaterialsList[j].SourceType);
str.AppendFormat("<td width='200'>{0}.{1}</td>", category.MaterialsList[j].SortNumb,
category.MaterialsList[j].Name);
str.AppendFormat("<td width='100'>{0}</td>", category.MaterialsList[j].Note);
str.Append("</tr>");
}
}
str.Append("</table>");
return str;
}
ExportData方法(页面请求,返回和下载文件)
/// <summary>
/// 导出数据
/// </summary>
public ActionResult ExportData(int processId)
{
var tableData = GetExpData(processId);
var tableString = GetExpString(tableData);
// 输出Excel
string fileName = tableData.Name + "材料清单";
Response.ClearContent();
Response.AddHeader("content-disposition",
string.Format("attachment; filename={0}.xls",
HttpUtility.UrlEncode(fileName, Encoding.UTF8)));
Response.ContentType = "application/excel";
Response.Charset = "utf-8";
Response.Write(tableString);
Response.End();
var myProcessId = processId;
return RedirectToAction("Index", new { processId = myProcessId });
}
这种方法省去了去调用繁琐的Excel API,非常方便。完全满足合并单元格(合并行、合并列)的需求。