在系统设计中,都会有新增数据的功能,像那种人力资源系统,就需要大量新增员工信息或者一些大量的资料,大量的数据使得操作员要逐条手动添加,还要不断地切换复制,增加了新增数据的成本,同时加大了新增数据错误的几率,所以有了导入功能,批量导入可以把大量数据一次性新增进系统,既可以提高准确性,又节省了人力。
如何做好导入功能,首先想要导入,那咱们得先有Excel表格的模板吧,要不然谁知道你新增的格式是怎么样的。哎,在做模板之前,我们得给项目中加入依赖吧,工具包、注解包、文件上传。光是加入依赖还不够,还得去配置。
<!-- 导入导出工具包,可以完成Excel导出、导入,Word导出 -->
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<version>3.2.0</version>
</dependency>
<!-- 基础注解包,作用与实体对象上,拆分后方便maven多工程的依赖管理-->
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-annotation</artifactId>
<version>3.2.0</version>
</dependency>
<!-- 文件上传 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
加入文件上传的配置,这个配置是指定上传的文件总大小不能超过100MB,不是限制单个文件大小,是所有文件的大小之和。文件上传的配置可以放在Spring-mvc.xml中。
<!-- 文件上传的配置 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 指定所上传文件的总大小不能超过100MB。注意maxUploadSize属性的限制不是针对单个文件,而是所有文件的容量之和 -->
<property name="maxUploadSize" value="102400"/>
</bean>
依赖加了,文件上传配置加了,那就先来看看jsp页面的代码,Excel模板先不急。页面中给个导入按钮,用button标签,在button标签中加入点击事件(οnclick=”ToLead()”),在JS里给个点击导入按钮的Layui询问框,若是不想要询问框也可以去掉不用,里面的代码都做了相应的解释。
// 执行导入
function ToLead(){
layer.confirm('请选择需要的操作', {
btn:['下载模板','导入数据','取消'],area:['320px','155px']
}, function (index){
// 下载模板的链接
window.location.href = "${pageContext.request.contextPath}/Vehicle/Exceling";
// 按钮“下载模板”的回调
layer.close(index);
}, function (index){
// 打开导入文件模态框
$("#dao").click();
// 按钮“导入数据”的回调
layer.close(index);
}, function (index){
// 按钮“取消”的回调
layer.close(index);
});
}
询问框里面的按钮,我们先实现下载模板,就是提供相关的Excel模板给客户或者操作员下载出来,方便填写好数据信息上传。通过给出的链接来跳到控制器,控制器再执行下载模板的方法,代码解释都在图片里了。
/**
* 导入训练场(模板下载)
* @param response
* @throws IOException
*/
@RequestMapping("/Exceling")
public void Exceling(HttpServletResponse response) throws IOException {
// new一个初始量为10的空的数组列表
List<Training> list = new ArrayList<>();
// 自定义Excel表格标题与工作表标签名
Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("训练场导入模板", "训练场"), Training.class, list);
// 下载模板的文件名
String filename = "训练场导入模板.xls";
//不设置编码中文无法显示
filename = URLEncoder.encode(filename, "UTF-8");
// 设置流的响应头
response.setHeader("Content-Disposition", "attachment;Filename=" + filename);
// 创建流
OutputStream outputStream = response.getOutputStream();
// 写入流
workbook.write(outputStream);
// 关闭流
outputStream.close();
}
还没完呢,要去实体类中给字段加上Excel注解,注解里加上需要的类型,如:字段名(name=)、列宽(width=)、替换类型(replace=)、校验字段(isImportField=),常用的还有导出类型(type=)、列高(height=)………还有其他的类型,请搜索“EasyPOI 详细教程以及注解的使用”。图中都是简单的设置Excel模板中的字段表头样式,实体类的get与set方法以及其他的,你们自行添加。
public class Training {
//训练场ID
private Integer trainingID;
/**
* 训练场名称
*/
@Excel(name = "训练场名称", width = 18,isImportField = "diyi")
private String trainingName;
/**
* 训练场号码
*/
@Excel(name = "训练场号码", width = 18,isImportField = "diyi")
private String trainingNumber;
/**
* 容纳车辆
*/
@Excel(name = "容纳车辆", width = 15,isImportField = "diyi")
private String carAccommodate;
/**
* 投放车辆
*/
@Excel(name = "投放车辆", width = 15,isImportField = "diyi")
private String putInCar;
/**
* 训练场地址
*/
@Excel(name = "训练场地址", width = 30,isImportField = "diyi")
private String trainingSite;
/**
* 训练场状态
*/
@Excel(name = "训练场状态", width = 15,replace = {"作废_0","启用_1"},isImportField = "diyi")
private Integer trainingState;
//车型ID
private Integer carTypeID;
/**
* 车型
*/
@Excel(name = "车型", width = 15,isImportField = "diyi")
private String carType;
//科目类型ID
private Integer subjectsTypeID;
/**
* 科目类型
*/
@Excel(name = "科目类型", width = 15,isImportField = "diyi")
private String subjectsType;
}
客户或者操作员有了模板后,填写好了信息准备上传,现在就该做上传功能了。询问框第二个按钮就是导入数据,通过点击“导入数据”按钮打开文件上传的模态框。在页面的导入按钮下面放一个隐藏的button按钮,用于打开文件上传模态框的按钮。
这个文件上传是调用了Layui的文件上传的插件,所以调用隐藏按钮的ID打开,还要去控制器写个文件上传的方法,值得注意的是size:0,允许上传的文件大小,可自定义设置文件大小。文件上传基础参数可以参考Layui的文件上传。选择好上传的文件后,判断上传的数据长度是否不等于0,不等于0就会获取预览框按钮打开并把获取到数据赋值进去,便可以在模态框中查看是否是你想要导入的数据。后面的两个是上传完毕的回调与上传异常的回调。
var layer, upload, tableIns;
/*声明Layui组件*/
layui.use(['table','layer','upload'],function (){
var table = layui.table;
layer = layui.layer;
upload = layui.upload;
//文件导入
upload.render({
elem: '#dao' //绑定元素
, url: '${pageContext.request.contextPath}/Vehicle/Exceled' //上传接口
, accept: 'file' //允许上传的文件类型
//规定打开文件选择框时,筛选出的文件类型
, acceptMime: 'application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
, exts: 'xls|xlsx' //允许上传的文件后缀。一般结合 accept 参数类设定
, auto: true //是否选完文件后自动上传
// , bindAction: '#bindAction'//指向一个按钮触发上传color
, size: 0 //最大允许上传的文件大小kb
//,method: 'post' //可选项。HTTP类型,默认post
, before: function (obj) { //obj参数包含的信息,跟 choose回调完全一致。
layer.load(); //上传loading
}
, done: function (res) {//执行上传请求后的回调
console.log(res);
if (res.length != 0) {// 预览
$("#upfile").click();//打开模态框
table.reload('demo1', {//赋值数据
data: res
});
}
//上传完毕回调
layer.closeAll('loading'); //关闭loading
return;//不可取消
}
, error: function () {//执行上传请求出现异常的回调
layer.closeAll('loading'); //关闭loading
//请求异常回调
return;//不可取消
}
});
});
文件上传接口,控制器方法:
/**
* 文件上传
* @param file
* @param session
* @return
* @throws Exception
*/
@RequestMapping("/Exceled")
@ResponseBody
public List<Training> Exceled(@RequestParam("file") CommonsMultipartFile[] file, HttpSession session) throws Exception {
ImportParams params = new ImportParams();
//设置标题与表头不需要导入
params.setTitleRows(1);
params.setHeadRows(1);
//开始导入数据
List<Training> list = ExcelImportUtil.importExcel(file[0].getInputStream(), Training.class, params);
//保存到session
session.setAttribute("supvolist", list);
//返回list
return list;
}
这是文件上传预览框,在预览框内放入提示与表格,表格是跟页面表格一样的,这样就能看到导入的数据是否跟页面表格一致。表格上一步的提示的可以自定义。确定没问题可以进行导入操作,给开始导入按钮一个点击事件(addfile( ))。注意:文件上传预览框是放在文件导入的上面的,都在Layui方法里面。
var layer, upload, tableIns;
/*声明Layui组件*/
layui.use(['table','layer','upload'],function (){
var table = layui.table;
layer = layui.layer;
upload = layui.upload;
/*文件上传预览数据表格*/
table.render({
elem: '#demo1',//表格ID
//url: '${pageContext.request.contextPath}/Vehicle/JumpThreePage',//数据接口
//data:
page: true,//开启分页
cols: [[
{type: 'numbers',title: '序号',fixed: 'left'},/*序列号*/
{field: 'trainingName',title: '训练场名称',align: 'center',width: 180},
{field: 'subjectsType',title: '科目',align: 'center',width: 113},
{field: 'carType',title: '培训车型',align: 'center',width: 100},
{field: 'trainingNumber',title: '联系电话',align: 'center',width: 160},
{field: 'trainingSite',title: '训练场地址',align: 'center',width: 280},
{templet: function (data){
return data.carAccommodate+" ➡ "+data.putInCar;
},title: '容纳 → 投放(辆) ',align: 'center',width: 180},
{field: 'trainingState',title: '训练场状态',align: 'center',width: 100,templet: '#trainingState'},
]],
/*解析原始数据格式*/
parseData:function (res){
return{
"code": 0, //解析接口状态
"msg": res.message, //解析提示文本
"count": res.length, //解析数据长度
"data": res //解析数据列表
};
}
});
});
点击开始导入执行post请求到控制器:
//保存导入
function addfile(){
//用post请求提交到控制器上传方法
$.post("${pageContext.request.contextPath}/Vehicle/savesup", {
AreyouOK: true//参数
}, function (data) {
layer.alert(data.msg);//Layui提示
//刷新表格
tableIns.reload({url:"${pageContext.request.contextPath}/Vehicle/JumpThreePage"});
$("#shishi").click();//关闭模态框
});
}
控制器执行导入方法并判断重要的数据:
/**
* 保存上传
* @param AreyouOK
* @param session
* @return
*/
@ResponseBody
@RequestMapping("/savesup")
public JsonReturn savesup(Boolean AreyouOK, HttpSession session) {
JsonReturn jsonReturn = new JsonReturn();
int a = 0;//成功
int b = 0;//名字相同
int c = 0;//关键数据错误
int d = 0;//必要数据不存在
if (AreyouOK) {
//取值
List<Training> list = (List<Training>) session.getAttribute("supvolist");
//判断是否有值
if (list != null) {
for (Training ting : list) {
if (ting == null) {
continue;//跳过单次循环
}//空列
//查询是否存在训练场名称
Training tr = vehicleService.TrainingName(ting.getTrainingName());
if (tr != null) {
b++;
continue;//跳过单次循环
}
//根据新增的科目类型查询对应的ID
SubjectsType su = vehicleService.SubjectsType(ting.getSubjectsType());
if (su == null) {
c++;
continue;
}
//根据新增的车型查询对应的ID
CarType ca = vehicleService.CarType(ting.getCarType());
if (ca == null) {
c++;
continue;
}
ting.setSubjectsTypeID(su.getSubjectsTypeID());//科目类型对应ID
ting.setCarTypeID(ca.getCarTypeID());//车型对应ID
// 判断必要数据不存在
if (ting.getTrainingName() == "" || ting.getTrainingName() == null
|| ting.getSubjectsTypeID() == null || ting.getSubjectsTypeID() == 0
|| ting.getCarTypeID() == null || ting.getCarTypeID() == 0){
d++;
continue;//跳过单次循环
}
// 执行新增
int it = vehicleService.insertTraining(ting);
if (it > 0) {
a++;
}
}
String su = String.format("共发现 " + list.size() + " 条数据<br>保存成功数 : " + a + "<br/>保存失败数 : " + (list.size() - a) + " <br>原因如下:<br>训练场名称相同数 : " + b + " <br>数据不完整数 : " + d + " <br>关键数据不存在数 : " + c);
jsonReturn.setState(true);
jsonReturn.setMsg(su);
session.removeAttribute("supvolist");
} else {
jsonReturn.setMsg("请先导入数据");
}
}
else {
jsonReturn.setMsg("请正确导入数据");
}
return jsonReturn;
}