aisell_EasyPOI 数据导入导出

一、Java操作办公软件

(1)java可以操作办公软件:
Word、Excel、PPT。其中Word和Excel操作最为频繁

(2)java操作Exccel
导入(磁盘数据读取到内存)导出

(3)java操作办公软件的框架
JXL:只操作Exccel,只支持office2003之前的,功能还是很强大的
POI:apatch公司的 ,支持word、Excel、ppt。它写了两套API,支持office2003及以前和office2007以后的,功能也很强大

二、POI导出和导入的使用

找到POI的文档。以下使用office2007版本的(注:网上有相应的兼容方案,可判断)
HSSF(xls)是2003版本的
XSSF(xlsx)是2007版本的
aisell_EasyPOI 数据导入导出_第1张图片
(1)使用POI的前提,导入POI的相应的jar包



  org.apache.poi
  poi
  3.11


  org.apache.poi
  poi-ooxml
  3.11

(2)POI的导出。使用POI创建一个Excel的九九乘法表

//创建Excel表--poi的导出
    @Test
    public void myTestCreatExccel() throws  Exception{

        //1.创建一个Excel表(在内存中)
        SXSSFWorkbook workbook = new SXSSFWorkbook();

        //2.再创建一张sheet表(Excel表下方有表sheet1、sheet2、....)
        Sheet sheet = workbook.createSheet("九九乘法表");

        //3.创建行
        for (int i=1;i<=9;i++){
            Row row = sheet.createRow(i - 1);//减去1,最边上的行不要有空的
            //4.创建列(格子)
            for (int j=1;j<=i;j++){
                Cell cell = row.createCell(j - 1);//减去1,最边上的列不要有空的
                //5.往格子中加数据
                cell.setCellValue(i+"*"+j+"="+(i*j));
            }
        }
        //6.写到内存中去
        FileOutputStream out = new FileOutputStream("template/99.xlsx");
        workbook.write(out);
        out.close();
        workbook.dispose();
    }

(3)POI的导入。读取数据到内存中来

	//POI的导入--读取数据
    @Test
    public void myTestReadExcel() throws  Exception{
        //1.把数据template文件路径下的emp-poi.xlsx文件读取到内存中
        Workbook workbook = WorkbookFactory.create(new File("template/emp-poi.xlsx"));
        //2.根据文件拿到对应的表--此处是根据下标获取
        Sheet sheet = workbook.getSheetAt(0);
        //3.拿到表的对应的所有行
            //LastRowNum:最后一行(总行数)
        int lastRowNum = sheet.getLastRowNum();
        //遍历所有行
        for(int i = 1; i <= lastRowNum; i++) {
            //拿到对应的行
            Row row = sheet.getRow(i);
            //4.拿到行对应的列(格子)
            //lastCellNum:最后一列(总列数)
            short cellNum = row.getLastCellNum();
            //遍历总列数
            for (int j = 0; j < cellNum; j++) {
                //5.拿到格子中的数据
                Cell cell = row.getCell(j);
                //获取不到文件中的int类型数据。--解决;在文件中的int类型打个‘ 转成string类型--很low
                System.out.print(cell.getStringCellValue() + " ");
            }
            System.out.println();
        }
    }

三、SpringMVC也有导入和导出功能

四、EasyPOI的导出和导入

原生的POI操作还是比较复杂,所以基于POI又做了一层封装,使操作更加简单。springmvc支持
EasyPOI的网上文档路径:http://easypoi.mydoc.io/
在maven中引入easyPOI
(1)先导入EasyPOI的jar包。
注意;要把之前引入的POI的jar包去掉,否则会有冲突



  cn.afterturn
  easypoi-base
  3.2.0


  cn.afterturn
  easypoi-web
  3.2.0


  cn.afterturn
  easypoi-annotation
  3.2.0

(2)测试一下EasyPOI导出
使用Excel注解版导入导出
先写一个domain类,在要导出到Excel的字段上打上@Excel注解
@Excel(name=“xx”,其他属性) 具体看文档,表示表头的信息
注意:

/*
* EasyPOI的准备类
* */
import cn.afterturn.easypoi.excel.annotation.Excel;

public class POIEmployee {
	@Excel(name = "编号")
    private Long id;
    //姓名
    @Excel(name = "用户名")//表头的名字
    private String username;
    //邮件
    @Excel(name = "邮箱",width = 20)//还可以扩展表格宽度
    private String email;
    //以下省略了get/set方法和tostring方法

测试类

/*
 * EasyPOI的测试类
 * */
public class EasyPOITest {
    @Test
    public void myTest() throws  Exception{
        //1.模拟数据库中的数据
        POIEmployee e1 = new POIEmployee();
        e1.setId(1L);
        e1.setUsername("张三");
        e1.setEmail("[email protected]");
        POIEmployee e2 = new POIEmployee();
        e2.setId(2L);
        e2.setUsername("李四狗");
        e2.setEmail("[email protected]");

        //2.将数据放到集合中去
        List list=new ArrayList<>();
        list.add(e1);
        list.add(e2);

        //3.将拿到的数据写到Excel中去,然后生成Excel
        /*
        * 以下都是从文档中拷贝的,但是要理解方法的用法,参数的意思
        * exportExcel:导出
        *    ExcelExportUtil.exportExcel:导出一个excel
        *    new ExportParams("表头","二级表头","表名")  也可以不用设置,默认Excel中的
        *    POIEmployee.class:引出的类是谁
        *       list:导出的数据
        * */
       //--------------拷贝过来稍微更改一部分就可用(类型、数据、表名、表头....)--------------------------
        Workbook wb = ExcelExportUtil.exportExcel(new ExportParams("员工数据","员工表"),
                POIEmployee.class, list);
      //-----------------------写死部分,直接拿来用---------------------------
        //4.将数据写到Excel文件中去--拿到输出流,会自动在template下创建个poiemp.xlsx文件
        FileOutputStream fileOutputStream = new FileOutputStream("template/poiemp.xlsx");
        //写到文件中去
        wb.write(fileOutputStream);
        //关闭流
        fileOutputStream.close();
    }
}

最后Excel表中的信息
aisell_EasyPOI 数据导入导出_第2张图片

(3)以上是简单的string类型的配置,那其他配置怎么使用呢?

  • sex是boolean值,我怎么让它true显示男,false展示女
  • bornDate是日期类型,我怎么完成它的格式化展示
  • 头像是图片,我们可以直接把图片进行导出嘛?

布尔类型配置显示性别,在上面实体类代码的基础上增加一个性别的布尔字段。在性别的注解@Excel中写个replace={“男_true”,“女_false”}—>true代表男,false代表女。也可以用int类型1、2表示,用法都是一样的

日期的格式化展示问题。format = “yyyy-MM-dd”:日期的格式 ,注意:如果数据库是varchar,还需要配置databaseFormat。

图片的导出。添加属性type = 2 :代表该字段类型为图片。默认的是type=1,不展示图片

实体类字段的注解@Excel配置

	@Excel(name = "编号")
    private Long id;

    //姓名
    @Excel(name = "用户名")//表头的名字
    private String username;

    //邮件
    @Excel(name = "邮箱",width = 20)//还可以扩展表格宽度
    private String email;

    //性别  true:男 false:女
    @Excel(name="性别", replace={"男_true","女_false"})
    private Boolean sex=true;

    //入职日期
    @Excel(name = "入职日期",format="yyyy-MM-dd")
    private Date bornDate = new Date();

    //头像图片
    @Excel(name = "头像",type = 2,height = 20,width = 20)
    private String headImage;
    //省略get/set方法

在模拟数据库的测试中

/*
 * EasyPOI的测试类
 * */
public class EasyPOITest {
    @Test
    public void myTest() throws  Exception{
        //1.模拟数据库中的数据
        POIEmployee e1 = new POIEmployee();
        e1.setId(1L);
        e1.setUsername("张三");
        e1.setEmail("[email protected]");
        e1.setSex(true);
        e1.setHeadImage("22.jpg");
        POIEmployee e2 = new POIEmployee();
        e2.setId(2L);
        e2.setUsername("李四狗");
        e2.setEmail("[email protected]");
        e2.setSex(false);
        e2.setHeadImage("44.jpg");

        //2.将数据放到集合中去
        List list=new ArrayList<>();
        list.add(e1);
        list.add(e2);

        //3.将拿到的数据写到Excel中去,然后生成Excel
        /*
        * 以下都是从文档中拷贝的,但是要理解方法的用法,参数的意思
        * exportExcel:导出
        *    ExcelExportUtil.exportExcel:导出一个excel
        *    new ExportParams("表头","二级表头","表名")  也可以不用设置,默认Excel中的
        *    POIEmployee.class:引出的类是谁
        *       list:导出的数据
        * */
    //稍微更改一部分代码。类型、数据、表名、表头....
        Workbook wb = ExcelExportUtil.exportExcel(new ExportParams("员工数据","员工表"),
                POIEmployee.class, list);
    //写死部分,以后要用直接拷贝过去用
        //4.将数据写到Excel文件中去--拿到输出流,会自动在template下创建个poiemp.xlsx文件
        FileOutputStream fileOutputStream = new FileOutputStream("template/poiemp.xlsx");
        //写到文件中去
        wb.write(fileOutputStream);
        //关闭流
        fileOutputStream.close();
    }
}

上面配置的效果展示
aisell_EasyPOI 数据导入导出_第3张图片

重要的配置:

  • 如果员工有一个对应的部门(多对一)字段,怎么怎么进行相应的展示。
    注解@ExcelEntity:标记是不是导出excel 标记为实体类。
    建一个部门的实体类POIDepartment
public class POIDepartment {

    //部门id
    private Long id;

    //部门名称
    @Excel(name = "部门名称")
    private String name;

    //部门地址
    @Excel(name = "部门地址")
    private String address="东北路2号";
	//省略get/set方法

在员工实体类中添加个部门的字段,并注解@ExcelEntity标记导出Excel部门为实体类

	 //部门
    @ExcelEntity//标记导出Excel的部门为实体类
    private POIDepartment department;
    //省略get/set方法

最后的效果展示图
aisell_EasyPOI 数据导入导出_第4张图片
需求:
既然两个实体类可以产生关联。我要在员工Excel表中部门的显示为:部门名称和部门地址。在部门Excel表中显示为:名称和地址,怎么办?
在类上使用@ExcelTarget注解:限制一个实体的注解,官方文档上有解释。
在员工实体类POIEmployee上打上@ExcelTarget注解

//员工实体类
@ExcelTarget("emp")
public class POIEmployee {
	//省略
}

还要在部门实体类上打上一样的注解

//部门实体类
@ExcelTarget("dept")
public class POIDepartment {
	//省略
}

然后部门实体类上相关联的字段上的注解@Excel(name=“xx”)稍微的改动

	//部门名称
    @Excel(name = "部门名称_emp,名称_dept")
    private String name;

    //部门地址
    @Excel(name = "部门地址_emp,地址_dept")//在emp员工Excel表中是部门地址,在dept部门Excel表中是地址
    private String address="东北路2号";
    //省略get/set方法

展示的效果图
部门的Excel表:
aisell_EasyPOI 数据导入导出_第5张图片
员工的Excel表:
aisell_EasyPOI 数据导入导出_第6张图片
那如果不想让员工Excel表看到部门地址,部门Excel能看到部门地址 ,怎么操作?
只需要将部门实体类的部门地址字段改动下即可。

	//部门地址
    @Excel(name = "地址_dept")//在emp员工Excel表中不显示,在dept部门Excel表中显示地址
    private String address="东北路2号";

效果图:
aisell_EasyPOI 数据导入导出_第7张图片
aisell_EasyPOI 数据导入导出_第8张图片

总体归纳一下:
给员工实体类中的部门实体类字段打注解@ExcelEntity,那么部门实体类的字段上打了@Excel注解的字段都会显示在员工Excel表中。如果想要两张表某些字段在不同表中的显示不一样,那么就要给每个实体类上打上注解@ExcelTarget(“xx”),表示实体类的名称,然后在需要调整的字段上做相应的调整即可。比如@Excel(name = “部门名称_emp,名称_dept”):表示在emp员工Excel表中显示的是部门名称,而在dept部门的Excel表中显示的是名称。但是如果想要某个字段不要显示在其他Excel表中,只显示在自己的Excel表中,就将上面的注解改为@Excel(name = “地址_dept”) :表示员工Excel表不显示这个字段,部门Excel表显示为地址。

(4)测试下easypoi的导入
准备好了一个poiemp.xlsx文件,也是被读取的文件

//easypoi导入
    @Test
    public void myTestRead() throws Exception{
        //设置导入的参数
        ImportParams params = new ImportParams();
        //准备的poiemp.xlsx文件,标题有一行(如果要读取的文件没有就不用设置)
        params.setTitleRows(1);
        //poiemp.xlsx文件,头也有一行(如果要读取的文件没有就不用设置)
        params.setHeadRows(1);
        
        /*
        * 从文档中拷贝过来,稍微更改:
        *   导入的类型,准备读取的文件,导入的参数
        * */
        List list = ExcelImportUtil.importExcel(
                new File("template/poiemp.xlsx"),
                POIEmployee.class, params);

        //遍历文件中的数据集合,打印读取出来
        for (POIEmployee employee : list) {
            System.out.println(employee);
        }
    }

读取的下效果:
文件中的所有信息全部读取出来,并且注意看下图片导入到电脑中的路径。图片会自动保存到项目的那个盘符中的excel文件中去。可以去找到它
在这里插入图片描述

五、将EasyPOI与项目结合起来—先导出

EasyPOI可以支持SpringMVC的
需求;要在员工页面新增一个导出功能。
导出的数据有:头像、用户名、邮件、年龄、部门
(1)由于EasyPOI可以支持SpringMVC,EasyPOI view项目是为了更简单的方便搭建在导出时候的操作,利用spring mvc的view封装,更加符合spring mvc的风格。
view下面包括多个 view的实现:

EasypoiBigExcelExportView 大数据量导出
EasypoiMapExcelView map 列表导出
EasypoiPDFTemplateView pdf导出
EasypoiSingleExcelView 注解导出
EasypoiTemplateExcelView 模板导出
EasypoiTemplateWordView word模板导出
MapGraphExcelView 图表导出

注意:
注解目录扫描的时候加上cn.afterturn.easypoi.view就可以使用了,让springmvc知道有这个Excel的view包。
(2)先在domain层的Employee类中的上述字段打上@Excel注解,注意还要在部门字段上打注解@ExcelEntity:easypoi标记导出Excel部门为实体类,这个 千万不能忘记。与部门domain类建立关联,与此同时还要 在部门domain类Department中的name字段上打上@Excel(name=“部门名称”)注解。
Employee.java部分

//头像
    @Excel(name = "头像",type = 2,height = 30)
    private String headImage;

    //用户名
    @Excel(name = "用户名")
    private String username;

    //邮件
    @Excel(name = "邮件",width = 25)
    private String email;

    //年龄
    @Excel(name = "年龄")
    private Integer age;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "department_id")
    @ExcelEntity//easypoi标记导出Excel部门为实体类
    private Department department;
    //省略get/set方法

Department.java部分

	@Excel(name = "部门名称")
    private String name;
   	//省略get/set方法

(3)准备一个到导出按钮
在员工页面Employee.jsp中设置一个按钮

导出

(4)在员工controller层EmployeeController写导出的路径代码

//导出按钮方法
    @RequestMapping("/export")
    public String export(ModelMap map){
        //从数据库中拿出到数据
        List list = iEmployeeService.findAll();

        //如何从数据库中拿出到数据变成Excel中的数据,并且导出到前台
        ExportParams params = new ExportParams("员工管理", "员工表", ExcelType.XSSF);
        // params.setFreezeCol(2);//冻结
        map.put(NormalExcelConstants.DATA_LIST, list); // 数据集合(导出的数据)
        map.put(NormalExcelConstants.CLASS, Employee.class);//导出实体
        map.put(NormalExcelConstants.PARAMS, params);//参数
        map.put(NormalExcelConstants.FILE_NAME, "员工表");//文件名称
        //return "easypoiExcelView"; /WEB-INF/views/easypoiExcelView.jsp
        return NormalExcelConstants.EASYPOI_EXCEL_VIEW;//View名称
    }

点击时报错:
aisell_EasyPOI 数据导入导出_第9张图片
分析:
返回的 easypoiExcelView源码:
controller层最后返回到这里,进行进一步的导出操作
aisell_EasyPOI 数据导入导出_第10张图片
(5)解决上述问题
在applicationContext-mvc.xml中配置扫描Excel的view包

	 
    

注意事项:
在applicationContext-mvc.xml中有个视图解析器,在扫描Excel的view包的时候会经过这个视图解析器,然后路径就会是这样的:/WEB-INF/views/easypoiExcelView.jsp,相当于扫描Excel的view包就被视图解析器给拦截了。
解决上述问题:
配置个bean的视图解析器 ,并且优先级在最前面,配置伪属性p:order=“0”:顺序在最前面,告诉springmvc先找配置的bean的视图解析器,也就是找到自己的源码easypoiExcelView,如果找不到再去找原来的视图解析器。
详解伪属性p:order=“0”:等同于这样写

 

伪属性的 p:order=“0”
在这里插入图片描述
最后效果
aisell_EasyPOI 数据导入导出_第11张图片
再来解决头像图片问题
分析:可能是路径问题造成的,所以告诉它图片的路径在哪即可。
拿到真实路径realpath

    //导出按钮方法
    @RequestMapping("/export")
    public String export(ModelMap map,  HttpServletRequest request){

        //从数据库中拿出员工数据
        List list = iEmployeeService.findAll();
 //----------------------------------------------------------------       
        //解决员工头像问题。拿到真实路径
        String realPath = request.getServletContext().getRealPath("");
        System.out.println(realPath);//H:\IDEA\idea_workspace\aisell\target\ssj
        //遍历拿到的数据集合
        list.forEach(e->{
            //拼接处图片的路径
            e.setHeadImage(realPath+e.getHeadImage());
        });
 //----------------------------------------------------------------       

        //如何从数据库中拿出到数据变成Excel中的数据,并且导出到前台
        ExportParams params = new ExportParams("员工管理", "员工表", ExcelType.XSSF);
        // params.setFreezeCol(2);//冻结
        map.put(NormalExcelConstants.DATA_LIST, list); // 数据集合(导出的数据)
        map.put(NormalExcelConstants.CLASS, Employee.class);//导出实体
        map.put(NormalExcelConstants.PARAMS, params);//参数
        map.put(NormalExcelConstants.FILE_NAME, "员工表");//文件名称
        //return "easypoiExcelView"; /WEB-INF/views/easypoiExcelView.jsp
        return NormalExcelConstants.EASYPOI_EXCEL_VIEW;//View名称
    }
}

效果图
aisell_EasyPOI 数据导入导出_第12张图片

功能需求:导出与高级查询结合,想要什么数据就导出想要的数据
在Employee.jsp中:
更改一下导出的提交方式:
form表单为post方法提交,并且给出action="/employee/export"的路径。
导出用按钮,不写type,默认就是submit提交方式。然后将form表单提交到后台。这样查询用的是a标签,并且执行的是查询方法,就不影响到导出功能

在后台EmployeeController中
既然是高级查询,那么就要拿到查询对象query,然后根据条件到后台去查询数据再执行导出方法
aisell_EasyPOI 数据导入导出_第13张图片
操作效果图:
在这里插入图片描述
aisell_EasyPOI 数据导入导出_第14张图片

六、将EasyPOI与项目结合起来—再导入

(1)准备一张要导入的Excel表。
注意:Excel表中的字段名称要跟domain类中打的注解@Excel(name=“名称”)一致,才能成功。
aisell_EasyPOI 数据导入导出_第15张图片
(2)新建一个导入的jsp页面import.jsp
根据视图解析器;在webapp/WEB-INF/views/import.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    Title
    <%@include file="/WEB-INF/views/head.jsp" %>



(3)controller层准备ImportController
第一:密码问题,Excel表中没有密码。给它个初始密码
第二:JPA的瞬时状态。由于表中有部门,但是没有部门的id,你保存员工时就有部门,但是没有部门id,部门的主键是空的。
解决办法:
根据对象拿到部门。
在DepartmentRepository

//根据用户名拿到部门
 Department findByName(String name);

在IDepartmentService

//根据用户名拿到部门
    Department findByName(String name);

在DepartmentServiceImpl

@Service
public class DepartmentServiceImpl extends BaseServiceImpl implements IDepartmentService {

    @Autowired
    private DepartmentRepository departmentRepository;

    @Override
    public Department findByName(String name) {
        return departmentRepository.findByName(name);
    }
}

ImportController层

/*
* easypoi导入的controller
* */
@Controller
@RequestMapping("/import")
public class ImportController {

    //注入IEmployeeService对象
    @Autowired
    private IEmployeeService employeeService;

    //注入IDepartmentService部门对象
    @Autowired
    private IDepartmentService departmentService;

    @RequestMapping("/index")
    public String index(){
        return "import";
    }

    //上传员工的Excel操作
    @RequestMapping("/employeeXlsx")
    public String employeeXlsx(MultipartFile empFile) throws Exception{

        //设置导入的参数
        ImportParams params = new ImportParams();
        //params.setTitleRows(1);//导入的Excel表没有标题头
        params.setHeadRows(1);
        List list = ExcelImportUtil.importExcel(
                //使用importExcel的流的参数
                empFile.getInputStream(),
                Employee.class, params);

        list.forEach(e-> {
//            System.out.println(e+","+e.getDepartment());
            //从数据库中查询部门--解决JPA的瞬时异常问题。Excel表中的部门没有id,只有部门名称。所以保持的是部门主键是空的
            String deptName = e.getDepartment().getName();
            System.out.println("-----"+deptName);
            //在根据部门名称去数据库查部门(数据库中的部门就有id值)
            Department dept = departmentService.findByName(deptName);
            e.setDepartment(dept);
            //设置初始密码--解决Excel表中没有密码字段的问题
            e.setPassword("123");
            //保持到数据库中
            employeeService.save(e);
            System.out.println("====="+e);
        });
        //导入成功后返回原页面
        return "import";
    }
}

效果图
aisell_EasyPOI 数据导入导出_第16张图片

七、导入的重要功能、需求。验证。自定义验证

准备的导入的一张表要满足一定的规则才能导入到数据库中,比如:名字和数据库中的名字不能为空、重复之类的 ,年龄不能大于多少或者小于多少等等验证,将导入失败的数据单独下载到Excel表中,并在每行失败数据后面注明导入失败原因。
(1)理解:Excel导入校验
校验,是一个不可或缺的功能,现在java校验主要是JSR 303 规范,实现方式主流的有两种
Hibernate Validator和Apache Commons Validator。

(2)导入JSR 303 规范的验证jar包



  org.hibernate
  hibernate-validator
  5.2.4.Final

(2)在相应的字段上加上注解

  • @NotNull:不为空的注解
  • @Max(value = 80,message = “xx不能大于80”)
  • 还有很多规范的注解,看官方文档

(3)准备一个自定义的验证规则类

/*
 * 导入Excel文件的自定义验证规则
 *   Excel表中的用户名不能和数据库中已有的用户名重复
 *   自定义的验证规则类要实现IExcelVerifyHandler接口类
 *   要让spring生成bean,加上注解@Component(其他层就用这个注解)
 * */
@Component//告诉spring这里要生成bean
public class EmployeeExcelVerifyHandler implements IExcelVerifyHandler {

    //注入IEmployeeService对象,才能在下面和数据库中的用户名进行验证
    @Autowired
    private IEmployeeService employeeService;

    @Override
    public ExcelVerifyHandlerResult verifyHandler(Employee employee) {
        //写逻辑验证用户名是否重复。直接调用之前写法的验证登录注册用户名是否重复的方法checkName
        boolean success = employeeService.checkName(employee.getUsername());
        if(success){ //true代表可以用
            //ExcelVerifyHandlerResult类:只有布尔类型的success字段和string类型的msg字段。看源码
            return new  ExcelVerifyHandlerResult(true);
        }
        return new ExcelVerifyHandlerResult(false,"用户名重复");
    }
}

(4)还要告诉ImportController,要在里面进行Excel导入的自定义的验证。
通过sprig注入自定义验证的bean对象

  • 扫描自定义验证所在的包,把它交给Spring管理
    在applicationcontext-mvc.xml中配置
 
    

(5)核心代码:ImportController部分


/*
* easypoi导入的controller
* */
@Controller
@RequestMapping("/import")
public class ImportController {

    //注入IEmployeeService对象
    @Autowired
    private IEmployeeService employeeService;

    //注入IDepartmentService部门对象
    @Autowired
    private IDepartmentService departmentService;

    //注入Excel导入的自定义验证的对象
    @Autowired
    private EmployeeExcelVerifyHandler employeeExcelVerifyHandler;

    @RequestMapping("/index")
    public String index(){
        return "import";
    }

    //上传员工的Excel操作--具有Excel导入验证功能
    @RequestMapping("/employeeXlsx")
    public String employeeXlsx(MultipartFile empFile) throws Exception{

        //设置导入的参数
        ImportParams params = new ImportParams();
        //params.setTitleRows(1);//导入的Excel表没有标题头
        //代表这里是需要验证
        params.setNeedVerfiy(true);
        params.setHeadRows(1);
        //加入自定义验证。告诉controller这里要进行自定义的验证
        params.setVerifyHandler(employeeExcelVerifyHandler);
        ExcelImportResult result = ExcelImportUtil.importExcelMore(
                //使用importExcel的流的参数
                empFile.getInputStream(),
                Employee.class, params);
        //拿到正确数据
        List list = result.getList();
        list.forEach(e->{
            System.out.println("正确的"+e);
        });
        //拿到错误数据
        List failList = result.getFailList();
        failList.forEach(e->{
            System.out.println("错误的"+e);
        });

(6)测试一下效果:
Excel表:admin数据库中已存在、第二个用户名为空、第四个年龄大于80岁
aisell_EasyPOI 数据导入导出_第17张图片
打印结果:
在这里插入图片描述

八、重中之重。将正确数据存到数据库,错误信息导出到Excel表并提示错误信息

需求:要将不符合的数据单独导出到一个Excel表中,并且后面给出失败的原因,合格的数据就直接添加到数据库中去。

  • 核心代码:ImportController部分
/*
* easypoi导入的controller
* */
@Controller
@RequestMapping("/import")
public class ImportController {

    //注入IEmployeeService对象
    @Autowired
    private IEmployeeService employeeService;

    //注入IDepartmentService部门对象
    @Autowired
    private IDepartmentService departmentService;

    //注入Excel导入的自定义验证的对象
    @Autowired
    private EmployeeExcelVerifyHandler employeeExcelVerifyHandler;

    @RequestMapping("/index")
    public String index(){
        return "import";
    }

    //上传员工的Excel操作--具有Excel导入验证功能
    @RequestMapping("/employeeXlsx")
    public String employeeXlsx(MultipartFile empFile, HttpServletResponse response) throws Exception{

        //设置导入的参数
        ImportParams params = new ImportParams();
        //params.setTitleRows(1);//导入的Excel表没有标题头
        //代表这里是需要验证
        params.setNeedVerfiy(true);
        params.setHeadRows(1);
        //加入自定义验证。告诉controller这里要进行自定义的验证
        params.setVerifyHandler(employeeExcelVerifyHandler);
        ExcelImportResult result = ExcelImportUtil.importExcelMore(
                //使用importExcel的流的参数
                empFile.getInputStream(),
                Employee.class, params);
    //拿到正确数据,然后将正确的数据保存到数据库中去
        List list = result.getList();
        list.forEach(e-> {
            //从数据库中查询部门--解决JPA的瞬时异常问题。Excel表中的部门没有id,只有部门名称。所以保持的是部门主键是空的
            String deptName = e.getDepartment().getName();
            //在根据部门名称去数据库查部门(数据库中的部门就有id值)
            Department dept = departmentService.findByName(deptName);
            e.setDepartment(dept);
            //设置初始密码--解决Excel表中没有密码字段的问题
            e.setPassword("123");
            //保持到数据库中
            employeeService.save(e);
        });

    //创建一个错误数据的Excel表
        Workbook failWorkbook = result.getFailWorkbook();
        //用下载功能把文件写出去(下载;把文件变成一个流并写出去,还要设置响应头,所以要用到响应对象)
        //下面设置好直接使用即可导出
        //设置响应的文件类型 mime类型
        //application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:指的是Excel2007版
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        //attachment:不要用浏览器打开(这是下载)
        response.setHeader("Content-disposition", "attachment;filename=empFail.xlsx");
        response.setHeader("Pragma", "No-cache");//设置不要缓存
        //拿到输出流
        OutputStream ouputStream = response.getOutputStream();
        //将错误信息写出去
        failWorkbook.write(ouputStream);
        //刷新流
        ouputStream.flush();
        //关闭流
        ouputStream.close();
        //导入成功后返回原页面
        return "import";
    }
}
  • 效果展示图:
    准备的Excel表也有上述的错误。
    aisell_EasyPOI 数据导入导出_第18张图片
    主页面上会弹出下载的Excel表
    aisell_EasyPOI 数据导入导出_第19张图片
    下载的Excel表就是导入失败的数据,后面还有失败原因
    aisell_EasyPOI 数据导入导出_第20张图片
    数据库中就是成功的数据
    在这里插入图片描述

掌握这个很重要,java经常操作办公软件,这个就很实用

你可能感兴趣的:(java操作办公软件—POI)