云小杰

相对于绝对成功的汲汲渴求,越无杂质的奔赴,越是动人。

要一个黄昏, 满是风, 和正在落下的夕阳。如此, 足够我爱这破碎泥泞的人间。


Download the theme

Easypoi

学习EasyPOI

1. 简介

官方网站:http://doc.wupaas.com/docs/easypoi/easypoi-1c0u6ksp2r091。

Easypoi 的目标不是替代 poi ,而是让一个不懂导入导出的快速使用 poi 完成 Excelword 的各种操作,而不是看很多 api 才可以完成这样工作。

2. 使用EasyPOI

2.1 环境搭建

引入相关依赖

        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-base</artifactId>
            <version>4.4.0</version>
        </dependency>
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-web</artifactId>
            <version>4.4.0</version>
        </dependency>
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-annotation</artifactId>
            <version>4.4.0</version>
        </dependency>

2.2 相关注解

**
> easypoi起因就是Excel的导入导出,最初的模板是实体和Excel的对应,model--row,filed--col 这样利用注解我们可以和容易做到excel到导入导出
**
举例:
java中有一个User类,属性有id、name、age等等;
那么
- User类代表Excel文件
- 一个User对象代表Excel文件中的一行
- User类的每个属性(id、name、age)代表Excel中的一列
  • @Excel 作用到 filed上面,是对 Excel一列的一个描述。用于类里面的属性。
  • @ExcelCollection 表示一个集合,主要针对一对多的导出,比如一个老师对应多个科目,科目就可以用集合表示。类里面的属性是一个集合。
  • @ExcelEntity 表示一个继续深入导出的实体,但他没有太多的实际意义,只是告诉系统这个对象里面同样有导出的字段。
  • @ExcelIgnore 和名字一样表示这个字段被忽略跳过这个导出。使用该注解的属性不会导出到Excel中。
  • @ExcelTarget 这个是作用于最外层的对象,描述这个对象的id,以便支持一个对象可以针对不同导出做出不同处理。用于对应Excel的那个类,需要定义一个唯一的标识

@Excel

属性 类型 默认值 功能
name String null 列名,支持name_id
needMerge boolean fasle 是否需要纵向合并单元格(用于含有list中,单个的单元格,合并list创建的多个row)
orderNum String “0” 列的排序,支持name_id
replace String[] {} 值得替换 导出是{a_id,b_id} 导入反过来
savePath String “upload” 导入文件保存路径,如果是图片可以填写,默认是upload/className/ IconEntity这个类对应的就是upload/Icon/
type int 1 导出类型 1 是文本 2 是图片,3 是函数,10 是数字 默认是文本
width double 10 列宽
height double 10 列高,后期打算统一使用@ExcelTarget的height,这个会被废弃,注意
isStatistics boolean fasle 自动统计数据,在追加一行统计,把所有数据都和输出 这个处理会吞没异常,请注意这一点
isHyperlink boolean false 超链接,如果是需要实现接口返回对象
isImportField boolean true 校验字段,看看这个字段是不是导入的Excel中有,如果没有说明是错误的Excel,读取失败,支持name_id
exportFormat String ”” 导出的时间格式,以这个是否为空来判断是否需要格式化日期
importFormat String ”” 导入的时间格式,以这个是否为空来判断是否需要格式化日期
format String ”” 时间格式,相当于同时设置了exportFormat 和 importFormat
databaseFormat String “yyyyMMddHHmmss” 导出时间设置,如果字段是Date类型则不需要设置 数据库如果是string 类型,这个需要设置这个数据库格式,用以转换时间格式输出
numFormat String ”” 数字格式化,参数是Pattern,使用的对象是DecimalFormat
imageType int 1 导出类型 1 从file读取 2 是从数据库中读取 默认是文件 同样导入也是一样的
suffix String ”” 文字后缀,如% 90 变成90%
isWrap boolean true 是否换行 即支持\n
mergeRely int[] {} 合并单元格依赖关系,比如第二列合并是基于第一列 则{0}就可以了
mergeVertical boolean fasle 纵向合并内容相同的单元格
fixedIndex int -1 对应excel的列,忽略名字
isColumnHidden boolean false 导出隐藏列

@ExcelTarget

属性 类型 默认值 功能
value String null 定义ID
height double 10 设置行高
fontSize short 11 设置文字大小

@ExcelEntity

属性 类型 默认值 功能
id String null 定义ID

@ExcelCollection

属性 类型 默认值 功能
id String null 定义ID
name String null 定义集合列名,支持nanm_id
orderNum int 0 排序,支持name_id
type Class<?> ArrayList.class 导入时创建对象使用

@ExcelIngore

忽略这个属性,多使用循环引用中。

3. 导出Excel

注意:导出Excel的对象必须实现序列化接口

3.1 导出基本数据

  1. 定义对象

    • @Data
      @ExcelTarget("users")
      public class User implements Serializable {
           
          @Excel(name = "编号", orderNum = "1")
          private String id;
          @Excel(name = "姓名", orderNum = "2")
          private String name;
          @Excel(name = "年龄", orderNum = "4", suffix = "$", replace = {"10岁_10", "12岁_12"}) //10岁替代10
          private Integer age;
          @Excel(name = "生日", width = 30.0, format = "yyyy-MM-dd HH:mm:ss", orderNum = "3")
          private Date birthday;
      }
      
  2. 测试数据

    • public class TestPOI {
           
          public List<User> getUser() {
              List<User> users = new ArrayList<>();
              for (int i = 0; i < 10; i++) {
                  User user = new User();
                  user.setId(String.valueOf(i));
                  user.setName("小杰" + i);
                  user.setAge(10 + i);
                  user.setBirthday(new Date());
                  users.add(user);
              }
              return users;
          }
           
          // 导出
          @Test
          public void testExport() throws IOException {
              List<User> users = getUser();
              // 导出Excel
              // ExcelExportUtil.exportExcel三个参数:
              // 参数1:ExportParams对象 参数2:导出的类型,即Excel对应的类 参数3:导出的数据
              Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("用户信息列表", "用户信息"), User.class, users);
              // 将Excel写出指定位置
              FileOutputStream outputStream = new FileOutputStream("D:/写代码/Java/LearnJava/LearnEasyPOI/src/test/java/test.xlsx");
              workbook.write(outputStream);
              outputStream.close();
              workbook.close();
          }
      }
      

image-20220330172408811

3.2 导出list集合

3.2.1 默认格式

@Data
@ExcelTarget("users")
public class User implements Serializable {
    //...
    @Excel(name = "爱好", width = 30.0, orderNum = "5")
    private List<String> hobbies;
}

默认格式如下:

image-20220330173750045

3.2.2 自定义导出格式

自定义导出格式可以在对应的 set 方法中进行处理

@Data
@ExcelTarget("users")
public class User implements Serializable {
    
    // ......

    //    @Excel(name = "爱好", width = 30.0, orderNum = "5")
    @ExcelIgnore
    private List<String> hobbies;

    @Excel(name = "爱好", width = 30.0, orderNum = "5")
    private String hobby;

    public String getHobby() {
        StringBuilder builder = new StringBuilder();
        hobbies.forEach(e -> {
            builder.append(e).append("、");
        });
        return builder.substring(0, builder.length() - 1).toString();
    }
}

image-20220330174312098

3.3 对象中含有对象

@Data
@ExcelTarget("cards")
public class Card implements Serializable {
    @Excel(name = "身份证号码", width = 20.0, orderNum = "6")
    private String number;
    @Excel(name = "籍贯", width = 40.0, orderNum = "7")
    private String address;
}

@Data
@ExcelTarget("users")
public class User implements Serializable {

    // ......
    @ExcelEntity // 标志对象之间
    private Card card;
}

image-20220330174931360

3.4 导出一对多关系

@Data
@NoArgsConstructor
@AllArgsConstructor
@ExcelTarget("orders")
public class Order implements Serializable {
    @Excel(name = "订单编号", orderNum = "8", width = 20.0)
    private String no;
    @Excel(name = "订单名称", orderNum = "9", width = 15.0)
    private String name;
}

@Data
@ExcelTarget("users")
public class User implements Serializable {
    
    // ......
    @ExcelCollection(name = "订单列表", orderNum = "8") // 一对多
    private List<Order> orders;
}

image-20220330192658773

3.5 导出图片

@Data
@ExcelTarget("users")
public class User implements Serializable {

	// ......
    @Excel(name = "头像", type = 2, width = 20)// 2表示图像
    private String photo;
}

3.6 大数据量导出

​ 大数据导出是当我们的导出数量在几万到上百万的数据时,一次从数据库查询这么多数据加载到内存然后写入会对我们的内存和CPU都产生压力,这个时候需要我们像分页一样处理导出分段写入 Excel 缓解 Excel 的压力。

@Test
    public void bigDataExport() throws Exception {

        List<MsgClient> list = new ArrayList<MsgClient>();
        Workbook workbook = null;
        Date start = new Date();
        ExportParams params = new ExportParams("大数据测试", "测试");
        for (int i = 0; i < 1000000; i++) {  //一百万数据量
            MsgClient client = new MsgClient();
            client.setBirthday(new Date());
            client.setClientName("小明" + i);
            client.setClientPhone("18797" + i);
            client.setCreateBy("JueYue");
            client.setId("1" + i);
            client.setRemark("测试" + i);
            MsgClientGroup group = new MsgClientGroup();
            group.setGroupName("测试" + i);
            client.setGroup(group);
            list.add(client);
            if(list.size() == 10000){
                workbook = ExcelExportUtil.exportBigExcel(params, MsgClient.class, list);
                list.clear();
            }
        }
        ExcelExportUtil.closeExportBigExcel();
        System.out.println(new Date().getTime() - start.getTime());
        File savefile = new File("D:/excel/");
        if (!savefile.exists()) {
            savefile.mkdirs();
        }
        FileOutputStream fos = new FileOutputStream("D:/excel/ExcelExportBigData.bigDataExport.xlsx");
        workbook.write(fos);
        fos.close();
    }

4. 导入Excel

4.1 基本使用

  1. 编写对应的实体类

    • @Data
      @ExcelTarget("Employee")
      public class Employee implements Serializable {
           
          @Excel(name = "编号")
          private String id;
          @Excel(name = "姓名")
          private String name;
          @Excel(name = "生日", format = "yyyy-MM-dd HH:mm:ss")
          private Date birthday;
          @Excel(name = "年龄")
          private Integer age;
      }
      
  2. 测试

    •     @Test
          public void testImport() throws Exception {
              ImportParams importParams = new ImportParams();
              importParams.setTitleRows(1); // 标题列占几行
              importParams.setHeadRows(2); //header占几行
              // 参数1:带入的excel文件流;参数2:导入类型;参数3:导入的配置对象
              List<Employee> objects = ExcelImportUtil.importExcel(new FileInputStream("D:/写代码/Java/LearnJava/LearnEasyPOI/src/test/java/test.xlsx"), Employee.class, importParams);
              objects.forEach(System.out::println);
          }
      
Employee(id=0, name=小杰0, birthday=Wed Mar 30 19:42:54 CST 2022, age=10)
Employee(id=1, name=小杰1, birthday=Wed Mar 30 19:42:54 CST 2022, age=11)
Employee(id=2, name=小杰2, birthday=Wed Mar 30 19:42:54 CST 2022, age=12)
Employee(id=3, name=小杰3, birthday=Wed Mar 30 19:42:54 CST 2022, age=13)
Employee(id=4, name=小杰4, birthday=Wed Mar 30 19:42:54 CST 2022, age=14)
Employee(id=5, name=小杰5, birthday=Wed Mar 30 19:42:54 CST 2022, age=152)
Employee(id=6, name=小杰6, birthday=Wed Mar 30 19:42:54 CST 2022, age=16)
Employee(id=7, name=小杰7, birthday=Wed Mar 30 19:42:54 CST 2022, age=17)
Employee(id=8, name=小杰8, birthday=Wed Mar 30 19:42:54 CST 2022, age=18)
Employee(id=9, name=小杰9, birthday=Wed Mar 30 19:42:54 CST 2022, age=19)

4.2 相关参数

属性 类型 默认值 功能
titleRows int 0 表格标题行数,默认0
headRows int 1 表头行数,默认1
startRows int 0 字段真正值和列标题之间的距离 默认0
keyIndex int 0 主键设置,如何这个cell没有值,就跳过 或者认为这个是list的下面的值 这一列必须有值,不然认为这列为无效数据
startSheetIndex int 0 开始读取的sheet位置,默认为0
sheetNum int 1 上传表格需要读取的sheet 数量,默认为1
needSave boolean false 是否需要保存上传的Excel
needVerfiy boolean false 是否需要校验上传的Excel
saveUrl String “upload/excelUpload” 保存上传的Excel目录,默认是 如 TestEntity这个类保存路径就是 upload/excelUpload/Test/yyyyMMddHHmss** 保存名称上传时间*五位随机数
verifyHanlder IExcelVerifyHandler null 校验处理接口,自定义校验
lastOfInvalidRow int 0 最后的无效行数,不读的行数
readRows int 0 手动控制读取的行数
importFields String[] null 导入时校验数据模板,是不是正确的Excel
keyMark String ”:” Key-Value 读取标记,以这个为Key,后面一个Cell 为Value,多个改为ArrayList
readSingleCell boolean false 按照Key-Value 规则读取全局扫描Excel,但是跳过List读取范围提升性能 仅仅支持titleRows + headRows + startRows 以及 lastOfInvalidRow
dataHanlder IExcelDataHandler null 数据处理接口,以此为主,replace,format都在这后面

4.3 Excel导入小功能

  1. 读取指定的sheet
    • 比如要读取上传得第二个sheet 那么需要把startSheetIndex = 1 就可以了
  2. 读取几个sheet
    • 比如读取前2个sheet,那么 sheetNum=2 就可以了
  3. 读取第二个到第五个sheet
    • 设置 startSheetIndex = 1 然后sheetNum = 4
  4. 读取全部的sheet sheetNum
    • 设置大点就可以了
  5. 保存Excel
    • 设置 needVerfiy = true,默认保存的路径为upload/excelUpload/Test/yyyyMMddHHmss 保存名称上传时间 五位随机数 如果自定义路径 修改下saveUrl 就可以了,同时saveUrl也是图片上传时候的保存的路径
  6. 判断一个Excel是不是合法的Excel
    • importFields 设置下值,就是表示表头必须至少包含的字段,如果缺一个就是不合法的excel,不导入

5. 集成web实现导入导出

5.1 环境搭建

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.8</version>
        </dependency>
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-base</artifactId>
            <version>4.4.0</version>
        </dependency>
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-web</artifactId>
            <version>4.4.0</version>
        </dependency>
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-annotation</artifactId>
            <version>4.4.0</version>
        </dependency>
spring:
  thymeleaf:
    cache: false
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/easypoi?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    username: root
    password: 7012+2
mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.lyj.easypoi_springboot.entity

5.2 导入功能

Controller

@Slf4j
@Controller
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    // 导入Excel文件
    @RequestMapping("/import")
    public String importExcel(MultipartFile excelFile) throws Exception {
        log.info("文件名称: [{}]", excelFile.getOriginalFilename());

        // excel导入
        ImportParams params = new ImportParams();
        params.setTitleRows(1);
        params.setHeadRows(1);
        List<User> objects = ExcelImportUtil.importExcel(excelFile.getInputStream(), User.class, params);
        objects.forEach(System.out::println);
        userService.saveAll(objects);

        return "redirect:/user/findAll";
    }

    // 查询所有
    @RequestMapping("/findAll")
    public String findAll(Model model){
        List<User> users = userService.findAll();
        model.addAttribute("users", users);
        return "index";
    }
}

Service

@Service
@Transactional
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDAO userDAO;

    // 查询所有
    @Override
    public List<User> findAll() {
        return userDAO.findAll();
    }

    // 导入Excel
    @Override
    public void saveAll(List<User> objects) {
        objects.forEach(user -> {
            user.setId(null);
            userDAO.save(user);
        });
    }
}

Mapper

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.lyj.easypoi_springboot.dao.UserDAO">
    <select id="findAll" resultType="com.lyj.easypoi_springboot.entity.User">
        select
            id,name,bir,no,address
        from
            t_user
    </select>

    <insert id="save" parameterType="com.lyj.easypoi_springboot.entity.User" useGeneratedKeys="true" keyProperty="id">
        insert into t_user values (#{id},#{name},#{bir},#{no},#{address})
    </insert>
</mapper>

image-20220331103644287

5.3 导出功能

    // 导出Excel文件
    @RequestMapping("/export")
    public void exportExcel(HttpServletResponse response) throws Exception {
        // 查询所有数据
        List<User> users = userService.findAll();
        //导出excel
        response.setHeader("content-disposition", "attachment;fileName=" + URLEncoder.encode("用户列表.xls", "UTF-8"));
        ServletOutputStream outputStream = response.getOutputStream();
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("学生列表信息", "学生信息"), User.class, users);
        workbook.write(outputStream);
        outputStream.close();
        workbook.close();
    }

image-20220331105639290

最近的文章

解码方法

91. 解码方法题目描述一条包含字母 A-Z 的消息通过以下映射进行了 编码 :'A' -> 1'B' -> 2...'Z' -> 26要 解码 已编码的消息,所有数字必须基于上述映射的方法,反向映射回字母(可能有多种方法)。例如,"11106" 可以映射为: "AAJF" ,将消息分组为 (1 1 10 6) "KJF" ,将消息分组为 (11 10 6)注意,消息不能分组为 (1 11 06) ,因为 "06" 不能映射为 "F" ,这是由于 "6" 和 "06"...…

继续阅读
更早的文章

Vueblog

VueBlog1. 后端项目搭建1.1 新建数据库CREATE TABLE `m_user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT,  `username` varchar(64) DEFAULT NULL,  `avatar` varchar(255) DEFAULT NULL,  `email` varchar(64) DEFAULT NULL,  `password` varchar(64) DEFAULT NULL,  `status...…

继续阅读