Spring Boot(五)基于Hutool的excel开发

835 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情

大家好! 我是慕歌,一只想教你学习 Spring Boot的野生coder! 欢迎来到慕歌的 Sping boot系列教程,希望通过这个教程带大家搭建基础的 Spring Boot项目,该教程所有知识点均来源于本人的真实开发!

前言

在前一节的分享中,慕歌向大家介绍了hutool 对我们开发的影响,以及如何使用基础的hutool 功能工具类辅助我们进行数据处理。这一节中慕歌将向大家介绍hutool 的实用用法,用hutool进行excel数据的导入,导出以及模板导出,这个功能在我们的后台开发中还是非常实用的功能。话不多说,下面开始正式教学。

导包:

excel 包完全依赖于hutool 工具包,不需要额外的引入,hutool all中已经包含了hutool 的全部工具包

        <!--   hutool工具类 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.22</version>
        </dependency>

开发:

首先确定我们要对那些表进行数据的导入导出,这里的建议是对数据更改不频繁的表进行数据的导入导出,而且对于数据的唯一性要求不高,否则在进行数据导入的过程中,需要进行复杂的逻辑处理。这里我使用的是组织的用户信息表,该表的数据只有组织自己进行维护,就可以使用标准导入。

/**
 * 组织用户信息表
 * @TableName cf_group_user
 */
@TableName(value ="cf_group_user")
@Data
public class GroupUser implements Serializable {
    /**
     * id
     */
    @TableId(type = IdType.AUTO)
    private Integer id;

    /**
     * 组织id
     */
    private Integer groupId;

    /**
     * 姓名
     */
    @NotBlank(message = "姓名不可为空")
    private String nickname;

    /**
     * 性别(0:默认,1:男,2:女)
     */
    @NotBlank(message = "性别不可为空")
    private Integer sex;

    /**
     * 出生日期
     */
    @NotBlank(message = "出生日期不可为空")
    private Date bornTime;

    /**
     * 形象照片
     */
    @NotBlank(message = "形象照片不可为空")
    private String photo;

    /**
     * 家庭地址
     */
    @NotBlank(message = "家庭地址不可为空")
    private String homeAddress;

    /**
     * 联系电话
     */
    @NotBlank(message = "联系电话不可为空")
    private Integer mobile;

    /**
     * 实名信息
     */
    private String authId;

    /**
     * 职务信息
     */
    @NotNull(message = "职务不可为空")
    private String post;
}
create table cf_group_user
(
    id           int unsigned auto_increment comment 'id'
        primary key,
    group_id     int unsigned               not null comment '组织id',
    nickname     char(30)                   not null comment '姓名',
    sex          tinyint unsigned           not null comment '性别(1:男,2:女)',
    born_time    datetime                   not null comment '出生日期',
    photo        varchar(255)               not null comment '形象照片',
    home_address varchar(255)               not null comment '家庭地址',
    mobile       int                        not null comment '联系电话',
    post         varchar(255)               not null comment '职务信息'
)
    comment '组织用户信息表' collate = utf8_unicode_ci;

create index auth_id
    on cf_group_user (auth_id);

create index group_id
    on cf_group_user (group_id);

create index mobile
    on cf_group_user (mobile);

在确定表后,我们需要选取那些字段是我们需要使用的,在excel 表中进行展示,需要用户填入,根据字段构建反射。

实现:

在工程中先确定使用的字段构建模板,根据字段构建excel 的模板。这里的示例如下:

//controller层
//返回模板
    @RequestMapping("/getExcelTemplate")
    public void getExcelTemplate(HttpServletResponse response) {
        groupUserService.getExcelTemplate(response);
    }

//sevice层
void getExcelTemplate(HttpServletResponse response);

//impl
@Override
    public void getExcelTemplate(HttpServletResponse response) {
        try {
            // 1 读取对象
            final ExcelReader reader = ExcelUtil.getReader(ResourceUtil.getStream("templates/group.xlsx"));
            List<List<Object>> lists = reader.read();
            ExcelWriter writer = ExcelUtil.getWriter(true);
            writer.write(lists);
            response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode("group.xlsx", "UTF-8"));
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            // 2 写出对象
            ServletOutputStream outputStream = response.getOutputStream();
            // 通过IO写出我们的表格对象
            writer.flush(outputStream, true);
            writer.close();
            IoUtil.close(outputStream);
        } catch (IOException e) {
            log.error("EducationServiceImpl [export] 输出到响应流失败", e);
            throw new APIException("导出Excel异常");
        }
    }

这里我使用的是三层构建,在controller 层中暴露接口,进行调用,所有的具体实现进行抽象。

//导入信息
    @RequestMapping("/importStudent")
    public R importStudent(@RequestParam MultipartFile file) {
        try {
            boolean userInfo = groupUserService.getUserInfo(file);
            if(userInfo) return R.success();
        } catch (IOException e) {
            log.error("EducationController [getEducation] 获取输入流失败", e);
            throw new APIException("获取输入流失败");
        }
        return R.error();
    }

//导出信息
    @RequestMapping("/export")
    public void export(@RequestBody PageVo pageVo, HttpServletResponse response) {
        groupUserService.export(pageVo, response);
    }
void export(PageVo pageVo, HttpServletResponse response);

boolean getUserInfo(MultipartFile file) throws IOException;

@Override
    public void export(PageVo pageVo, HttpServletResponse response) {
        // 从数据库查出数据对象封装成map
        final List<Map<String, Object>> educationList = this.page(new Page<>(pageVo.getPage(), pageVo.getLimit()), Wrappers.lambdaQuery()).getRecords()
                .stream()
                // 封装成 Map 并且放入 List
                .map(item -> {
                    final Map<String, Object> map = new LinkedHashMap<>();
                    // 错误,这里需要根据表中字段名称进行命名
                    map.put("nickname", item.getNickname());
                    map.put("sex", item.getSex());
                    map.put("mobile", item.getMobile());
                    map.put("bornTime", item.getBornTime());
                    map.put("homeAddress", item.getHomeAddress());
                    map.put("post", item.getPost());
                    return map;
                })
                .collect(Collectors.toList());
        // 准备将数据集合封装成Excel对象
        ExcelWriter writer = ExcelUtil.getWriter(true);
        // 通过工具类创建writer并且进行别名
        writer.addHeaderAlias("nickname", "姓名");
        writer.addHeaderAlias("sex", "性别( 0 表示男 , 1 表示 女)");
        writer.addHeaderAlias("mobile", "电话");
        writer.addHeaderAlias("bornTime", "出生日期");
        writer.addHeaderAlias("homeAddress", "家庭地址");
        writer.addHeaderAlias("post", "职位");
        // 准备将对象写入我们的 List
        writer.write(educationList, true);
        try {
            // 获取我们的输出流
            final OutputStream output = response.getOutputStream();
            response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode("group.xlsx", "UTF-8"));
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            writer.flush(output, true);
            writer.close();
            // 这里可以自行关闭资源或者写一个关闭资源的工具类
            IoUtil.close(output);
        } catch (IOException e) {
            log.error("EducationServiceImpl [export] 输出到响应流失败", e);
            throw new APIException("导出Excel异常");
        }
    }

    @Override
    public boolean getUserInfo(MultipartFile file) throws IOException {
        ExcelReader reader = ExcelUtil.getReader(file.getInputStream());
        HashMap<String, String> head = new HashMap<>(6);
        head.put("姓名","nickname");
        head.put("性别( 0 表示男 , 1 表示 女)","sex");
        head.put("电话","mobile");
        head.put("出生日期", "bornTime");
        head.put("家庭地址","homeAddress");
        head.put("职位","post");
        reader.setHeaderAlias(head);
        List<GroupUser> read = reader.read(0, 1, GroupUser.class);
        Group group = groupService.getOne(new QueryWrapper<Group>().eq("uid", UserConstant.USER_ID));
        for (GroupUser user : read) {
            user.setGroupId(group.getId());
            //TODO 默认图片
            user.setPhoto("https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png");
            user.setStatus(1);
            user.setCreateTime(new DateTime());
            if(!this.save(user))return false;
        }
        return true;
    }

使用hutool 进行excel 处理的时候,需要自己定义字段与excel 表头见的映射关系,这一点是比较麻烦的。但相比于其他excel的使用方式,这是一种非常简单的实现。

结语

这一章的分享到这里就结束了,下一节中还将带来邮件Mail服务的分享!
如果您觉得本文不错,欢迎点赞支持,您的关注是我坚持的动力!