系列目录
昨天文章太过仓促没有补充的示例源码,在者当时弄到到很晚没时间做出导出功能,对阅读理解造成影响,现补充一份示例源码,顺便补充导出的功能说明,望理解
示例代码下载 https://yunpan.cn/crtht5mukavwh 访问密码 0a47
ps:vs数据库脚本在解压目录下,修改web.config数据库链接,示例代码包含:,导出,上传
导入导出实在多例子,很多成熟的组建都分装了导入和导出,这一节演示利用linqtoexcel组件对excel的导入,这个是一个极其简单的例子。
我并不是说导入的简单。而是linqtoexcel让我们对excel操作更加简单!
最后我们将利用closedxml输出excel。这个比现流行npoi与epplus更加优秀的组件,以open xml sdk为基础,所以只支持xlsx,不支持xls格式(现阶段谁没有个office2007以上版本)
他导出的excel根据官方描述,兼容性远超同行对手
如果你不是使用本架构只看2,3,4点,使用bll层的代码,这同样适用你的mvc程序
linqtoexcel组件读取excel文件
closedxml组件输出excel
- 一张演示的数据库表
安装linqtoexcel nuget包
文件上传样例
closexml导出excel
1.数据表
create table [dbo].[spl_person](
[id] [nvarchar](50) not null, --id
[name] [nvarchar](50) null, --姓名
[sex] [nchar](10) null, --性别
[age] [int] null, --年龄
[idcard] [nvarchar](50) null, --idcard
[phone] [nvarchar](50) null, --电话
[email] [nvarchar](200) null, --邮件
[address] [nvarchar](300) null, --地址
[createtime] [datetime] not null, --创建时间
[region] [nvarchar](50) null, --区域
[category] [nvarchar](50) null, --类别
constraint [pk_spl_person] primary key clustered
(
[id] asc
)with (pad_index = off, statistics_norecompute = off, ignore_dup_key = off, allow_row_locks = on, allow_page_locks = on) on [primary]
) on [primary] go
如何使用这个框架?
按照之前的做法,更新到ef。并利用t4生成dal,bll,model。再用代码生成器生成界面复制进凯发娱发k8的解决方案,一步到位
配置好访问地址和权限,直接运行
再手动在工具栏添加导入和导出的按钮(别忘记添加权限)
@html.toolbutton("btnimport", "fa fa-level-down", resource.import, perm, "import", true)
@html.toolbutton("btnexport", "fa fa-level-up", resource.export, perm, "export", true)
2.安装linqtoexcel包
因为我们读取excel放在bll层,所有在bll层安装linqtoexcel包
3.文件上传
(这一点简单带过,可以到网上下载上传代码植入到自己系统中)
或者下载第32节的源码 或者下载本节的示例代码都可以
我这里使用普通的form上传功能
添加导入前端代码
导入按钮事件只要弹出上传框就好
$("#btnimport").click(function () {
$("#uploadexcel").window({ title: '@resource.import', width: 450, height: 160, iconcls: 'icon-details' }).window('open');
});
保证上传是成功的。
直接查看源码的c#上传代码
-------------------------------------------------------------------------------------------------------上面只是前期的准备工作--------------------------------------------------------------
在业务层添加以下代码
using apps.common;
using apps.models;
using apps.models.spl;
using linqtoexcel;
using system;
using system.collections.generic;
using system.io;
using system.linq;
using system.text;
using system.threading.tasks; namespace apps.spl.bll
{
public partial class spl_productbll
{
///
/// 校验excel数据
///
public bool checkimportdata( string filename, listpersonlist,ref validationerrors errors )
{ var targetfile = new fileinfo(filename); if (!targetfile.exists)
{ errors.add("导入的数据文件不存在");
return false;
} var excelfile = new excelqueryfactory(filename); //对应列头
excelfile.addmapping(x => x.name, "name");
excelfile.addmapping(x => x.sex, "sex");
excelfile.addmapping(x => x.age, "age");
excelfile.addmapping(x => x.idcard, "idcard");
excelfile.addmapping(x => x.phone, "phone");
excelfile.addmapping(x => x.email, "email");
excelfile.addmapping(x => x.address, "address");
excelfile.addmapping(x => x.region, "region");
excelfile.addmapping(x => x.category, "category");
//sheetname
var excelcontent = excelfile.worksheet(0); int rowindex = 1; //检查数据正确性
foreach (var row in excelcontent)
{
var errormessage = new stringbuilder();
var person = new spl_personmodel(); person.id =
person.name = row.name;
person.sex = row.sex;
person.age = row.age;
person.idcard = row.idcard;
person.phone = row.phone;
person.email = row.email;
person.address = row.address;
person.region = row.region;
person.category = row.category; if (string.isnullorwhitespace(row.name))
{
errormessage.append("name - 不能为空. ");
} if (string.isnullorwhitespace(row.idcard))
{
errormessage.append("idcard - 不能为空. ");
} //=============================================================================
if (errormessage.length > 0)
{
errors.add(string.format(
"第 {0} 列发现错误:{1}{2}",
rowindex,
errormessage,
"
"));
}
personlist.add(person);
rowindex = 1;
}
if (errors.count > 0)
{
return false;
}
return true;
} ///
/// 保存数据
///
public void saveimportdata(ienumerablepersonlist)
{
try
{
dbcontainer db = new dbcontainer();
foreach (var model in personlist)
{
spl_person entity = new spl_person();
entity.id = resulthelper.newid;
entity.name = model.name;
entity.sex = model.sex;
entity.age = model.age;
entity.idcard = model.idcard;
entity.phone = model.phone;
entity.email = model.email;
entity.address = model.address;
entity.createtime = resulthelper.nowtime;
entity.region = model.region;
entity.category = model.category;
db.spl_person.add(entity);
}
db.savechanges();
}
catch (exception ex)
{
throw;
}
}
}
}
bll
public class validationerrors : list
{
///
/// 添加错误
///
/// 信息描述
public void add(string errormessage)
{
base.add(new validationerror { errormessage = errormessage });
}
///
/// 获取错误集合
///
public string error
{
get {
string error = "";
this.all(a => {
error = a.errormessage;
return true;
});
return error;
}
}
}
validationerror
代码包含两个方法
public bool checkimportdata( string filename, list
filename为我们上传的文件。
personlist为承接数据list
validationerrors 错误集合
public void saveimportdata(ienumerable
保存数据
别忘记添加接口
public partial interface ispl_personbll
{
bool checkimportdata(string filename, listpersonlist, ref validationerrors errors);
void saveimportdata(ienumerablepersonlist);
}
简单明白,直接看代码,不再解析。ok这样控制器就可以直接调用了
public actionresult import(string filepath)
{
var personlist = new list();
//校验数据is
bool checkresult = m_bll.checkimportdata(filepath, personlist, ref errors);
//校验通过直接保存
if (checkresult)
{
m_bll.saveimportdata(personlist);
loghandler.writeservicelog(getuserid(),"导入成功", "成功", "导入", "spl_person");
return json(jsonhandler.createmessage(, resource.insertsucceed));
}
else
{
string errorcol = errors.error;
loghandler.writeservicelog(getuserid(), errorcol, "失败", "导入", "spl_person");
return json(jsonhandler.createmessage(, resource.insertfail errorcol));
} }
最后前端还需要把路径给回来。
function importdata()
{
$.post("@url.action("import")?filepath=" $("#txtexcelpath").val(), function (data) {
if (data.type == ) {
$("#list").datagrid('load');
$('#uploadexcel').window('close');
}
$.messagebox5s('@resource.tip', data.message); }, "json");
}
ok测试一下!建立一个新的excel格式
一般情况下我们是提供模版给用户下载供用户输入数据,来确保格式的正确性
--------------------------------------------------------------------------------------导出功能------------------------------------------------------------------------------
4.安装closedxml nuget包
在控制器添加以下代码:
public actionresult export()
{
var exportspource = this.getexportdata();
var dt = jsonconvert.deserializeobject(exportspource.tostring()); var exportfilename = string.concat(
"person",
datetime.now.tostring("yyyymmddhhmmss"),
".xlsx"); return new exportexcelresult
{
sheetname = "人员列表",
filename = exportfilename,
exportdata = dt
};
} private jarray getexportdata()
{
listlist = m_bll.getlist(ref setnopagerascbyid, "");
jarray jobjects = new jarray(); foreach (var item in list)
{
var jo = new jobject();
jo.add("id", item.id);
jo.add("name", item.name);
jo.add("sex", item.sex);
jo.add("age", item.age);
jo.add("idcard", item.idcard);
jo.add("phone", item.phone);
jo.add("email", item.email);
jo.add("address", item.address);
jo.add("createtime", item.createtime);
jo.add("region", item.region);
jo.add("category", item.category);
jobjects.add(jo);
}
return jobjects;
}
注意:exportexcelresult
此类是使用closedxml.excel,已经封装好了。大家直接拿来用就可以。把关注点都放在业务中
using closedxml.excel;
using system;
using system.data;
using system.io;
using system.text;
using system.web;
using system.web.mvc; namespace apps.web.core
{
public class exportexcelresult : actionresult
{
public string sheetname { get; set; }
public string filename { get; set; }
public datatable exportdata { get; set; } public exportexcelresult()
{ } public override void executeresult(controllercontext context)
{
if (exportdata == null)
{
throw new invaliddataexception("exportdata");
}
if (string.isnullorwhitespace(this.sheetname))
{
this.sheetname = "sheet1";
}
if (string.isnullorwhitespace(this.filename))
{
this.filename = string.concat(
"exportdata_",
datetime.now.tostring("yyyymmddhhmmss"),
".xlsx");
} this.exportexceleventhandler(context);
} ///
/// exports the excel event handler.
///
/// the context.
private void exportexceleventhandler(controllercontext context)
{
try
{
var workbook = new xlworkbook(); if (this.exportdata != null)
{
context.httpcontext.response.clear(); // 编码
context.httpcontext.response.contentencoding = encoding.utf8; // 设置网页contenttype
context.httpcontext.response.contenttype =
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; // 导出名字
var browser = context.httpcontext.request.browser.browser;
var exportfilename = browser.equals("firefox", stringcomparison.ordinalignorecase)
? this.filename
: httputility.urlencode(this.filename, encoding.utf8); context.httpcontext.response.addheader(
"content-disposition",
string.format("attachment;filename={0}", exportfilename)); // add all datatables in the dataset as a worksheets
workbook.worksheets.add(this.exportdata, this.sheetname); using (var memorystream = new memorystream())
{
workbook.saveas(memorystream);
memorystream.writeto(context.httpcontext.response.outputstream);
memorystream.close();
}
}
workbook.dispose();
}
catch (exception ex)
{
throw;
}
}
}
}
本节知识点,全部聚集在checkimportdata方法上。
对应列头是模版xlsx的列头
1.如果模版需要是是中文的,如name=名字,那么方法应该这么写
excelfile.addmapping
2.导入第几个sheet工作薄可以这么写
我这里写0是指第一个sheet工作薄。可以直接指定工作薄
var excelcontent = excelfile.worksheet
3.检查正确性可以确保数据的来源。可以给出用户正确的修改提示。
4.借助closedxml,导出实际只需要几行代码。哈哈..这是如此的简单。
return new exportexcelresult
{
sheetname = "人员列表",
filename = exportfilename,
exportdata = dt
};
2017-03-08 新增功能:导入前编辑,在导入之前会弹出编辑行的页面,让用户编辑数据或新增数据后决定是否提交
谢谢大家