简书三(SpringBoot实现Excel导入导出)

想实现Excel报表的导入导出,首先想到的就是POI,POI的API确实麻烦,需要我们逐行解析。

EasyPoi一款非常好用的Excel导入导出工具。

EasyPoi正是这么一款工具,如果你不太熟悉POI,想简单地实现Excel操作,用它就对了!

EasyPoi的目标不是替代POI,而是让一个不懂导入导出的人也能快速使用POI完成Excel的各种操作,而不是看很多API才可以完成这样的工作。

在SpringBoot中集成EasyPoi非常简单,只需添加如下一个依赖即可,真正的开箱即用!

1.添加依赖

<dependency>

    <group>cn.afterturn</group>

    <artifactId>easypoi-spring-boot-starter</artifactId>

    <version>4.4.0</version>

</dependency>

2.使用场景

以会员信息和订单信息的导入导出为例,分别实现下简单的单表导出和具有关联信息的复杂导出。

3.简单导出

以会员信息列表导出为例,使用EasyPoi来实现下导出功能,看看是不是够简单!

首先创建一个会员对象Member,封装会员信息;

@Data

@EqualsAndHashCode(callSuper = false)

public classMember{

    @Excel(name = "ID", width = 10)

    private Long id;

    @Excel(name = "用户名", width = 20, needMerge = true)

    private String username;

    private String password;

    @Excel(name = "昵称", width = 20, needMerge = true)

    private String nickname;

    @Excel(name = "出生日期", width = 20, format = "yyyy-MM-dd")

    private Date birthday;

    @Excel(name = "手机号", width = 20, needMerge = true, desensitizationRule = "3_4")

    private String phone;

    private String icon;

    @Excel(name = "性别", width = 10, replace = {"男_0", "女_1"})

    private Integer gender;

}

在此我们就可以看到EasyPoi的核心注解@Excel,通过在对象上添加@Excel注解,可以将对象信息直接导出到Excel中去,下面对注解中的属性做个介绍;

name:Excel中的列名;

width:指定列的宽度;

needMerge:是否需要纵向合并单元格;

format:当属性为时间类型时,设置时间的导出导出格式;

desensitizationRule:数据脱敏处理,3_4表示只显示字符串的前3位和后4位,其他为*号;

replace:对属性进行替换;

suffix:对数据添加后缀。

接下来我们在Controller中添加一个接口,用于导出会员列表到Excel,具体代码如下;

@Controller

@Api(tags = "EasyPoiController", description = "EasyPoi导入导出测试")

@RequestMapping("/easyPoi")

public classEasyPoiController{

  @ApiOperation(value = "导出会员列表Excel")

  @RequestMapping(value = "/exportMemberList", method = RequestMethod.GET)

   publicvoidexportMemberList(ModelMap map,HttpServletRequest request,HttpServletResponse res        ponse){

       List<Member> memberList = LocalJsonUtil.getListFromJson("json/members.json", Member.clas);

       ExportParams params = new ExportParams("会员列表", "会员列表", ExcelType.XSSF);

       map.put(NormalExcelConstants.DATA_LIST, memberList);

       map.put(NormalExcelConstants.CLASS, Member.class);

       map.put(NormalExcelConstants.PARAMS, params);

       map.put(NormalExcelConstants.FILE_NAME, "memberList");

       PoiBaseView.render(map, request, response, NormalExcelConstants.EASYPOI_EXCEL_VIEW);

    }

}

LocalJsonUtil工具类,可以直接从resources目录下获取JSON数据并转化为对象,例如此处使用的members.json;

运行项目,直接通过Swagger访问接口,注意在Swagger中访问接口无法直接下载,需要点击返回结果中的下载按钮才行,访问地址:http://localhost:8088/swagger-ui/

下载完成后,查看下文件,一个标准的Excel文件已经被导出了。

4.简单导入

在Controller中添加会员信息导入的接口,这里需要注意的是使用@RequestPart注解修饰文件上传参数,否则在Swagger中就没法显示上传按钮了;

@Controller

@Api(tags = "EasyPoiController", description = "EasyPoi导入导出测试")

@RequestMapping("/easyPoi")

public classEasyPoiController{

    @ApiOperation("从Excel导入会员列表")

    @RequestMapping(value = "/importMemberList", method = RequestMethod.POST)

    @ResponseBody

    publicCommonResultimportMemberList(@RequestPart("file")MultipartFile file){

        ImportParams params = new ImportParams();

        params.setTitleRows(1);

        params.setHeadRows(1);

        try {

            List<Member> list = ExcelImportUtil.importExcel(

                    file.getInputStream(),

                    Member.class,params);

            return CommonResult.success(list);

        } catch (Exception e) {

            e.printStackTrace();

            return CommonResult.failed("导入失败!");

        }

    }

}

然后在Swagger中测试接口,选择之前导出的Excel文件即可,导入成功后会返回解析到的数据。

5.复杂导出

当然EasyPoi也可以实现更加复杂的Excel操作,比如导出一个嵌套了会员信息和商品信息的订单列表,下面我们来实现下!

首先添加商品对象Product,用于封装商品信息;

@Data

@EqualsAndHashCode(callSuper = false)

public classProduct{

    @Excel(name = "ID", width = 10)

    private Long id;

    @Excel(name = "商品SN", width = 20)

    private String productSn;

    @Excel(name = "商品名称", width = 20)

    private String name;

    @Excel(name = "商品副标题", width = 30)

    private String subTitle;

    @Excel(name = "品牌名称", width = 20)

    private String brandName;

    @Excel(name = "商品价格", width = 10)

    private BigDecimal price;

    @Excel(name = "购买数量", width = 10, suffix = "件")

    private Integer count;

}

然后添加订单对象Order,订单和会员是一对一关系,使用@ExcelEntity注解表示,订单和商品是一对多关系,使用@ExcelCollection注解表示,Order就是我们需要导出的嵌套订单数据;

@Data

@EqualsAndHashCode(callSuper = false)

public classOrder{

    @Excel(name = "ID", width = 10,needMerge = true)

    private Long id;

    @Excel(name = "订单号", width = 20,needMerge = true)

    private String orderSn;

    @Excel(name = "创建时间", width = 20, format = "yyyy-MM-dd HH:mm:ss",needMerge = true)

    private Date createTime;

    @Excel(name = "收货地址", width = 20,needMerge = true )

    private String receiverAddress;

    @ExcelEntity(name = "会员信息")

    private Member member;

    @ExcelCollection(name = "商品列表")

    private List<Product> productList;

}

接下来在Controller中添加导出订单列表的接口,由于有些会员信息我们不需要导出,可以调用ExportParams中的setExclusions方法排除掉;

@Controller

@Api(tags = "EasyPoiController", description = "EasyPoi导入导出测试")

@RequestMapping("/easyPoi")

public classEasyPoiController{

@ApiOperation(value = "导出订单列表Excel")

@RequestMapping(value = "/exportOrderList", method = RequestMethod.GET)

publicvoidexportOrderList(ModelMap map,HttpServletRequest request,HttpServletResponse response){

      List<Order> orderList = getOrderList();

      ExportParams params = new ExportParams("订单列表", "订单列表", ExcelType.XSSF);

      //导出时排除一些字段

      params.setExclusions(new String[]{"ID", "出生日期", "性别"});

      map.put(NormalExcelConstants.DATA_LIST, orderList);

      map.put(NormalExcelConstants.CLASS, Order.class);

      map.put(NormalExcelConstants.PARAMS, params);

      map.put(NormalExcelConstants.FILE_NAME, "orderList");

      PoiBaseView.render(map, request, response, NormalExcelConstants.EASYPOI_EXCEL_VIEW);

    }

}

在Swagger中访问接口测试,导出订单列表对应Excel;

6.自定义处理

如果你想对导出字段进行一些自定义处理,EasyPoi也是支持的,比如在会员信息中,如果用户没有设置昵称,我们添加下暂未设置信息。

我们需要添加一个处理器继承默认的ExcelDataHandlerDefaultImpl类,然后在exportHandler方法中实现自定义处理逻辑;

public classMemberExcelDataHandlerextendsExcelDataHandlerDefaultImpl{

  @Override

  publicObjectexportHandler(Member obj, String name, Object value){

    if("昵称".equals(name)){

      String emptyValue = "暂未设置";

      if(value==null){

        return super.exportHandler(obj,name,emptyValue);

      }

      if(value instanceof String&&StrUtil.isBlank((String) value)){

        return super.exportHandler(obj,name,emptyValue);

      }

    }

    return super.exportHandler(obj, name, value);

  }

  @Override

  publicObjectimportHandler(Member obj, String name, Object value){

    return super.importHandler(obj, name, value);

  }

}

然后修改Controller中的接口,调用MemberExcelDataHandler处理器的setNeedHandlerFields设置需要自定义处理的字段,并调用ExportParams的setDataHandler设置自定义处理器;

@Controller

@Api(tags = "EasyPoiController", description = "EasyPoi导入导出测试")

@RequestMapping("/easyPoi")

public classEasyPoiController{

    @ApiOperation(value = "导出会员列表Excel")

    @RequestMapping(value = "/exportMemberList", method = RequestMethod.GET)

    publicvoidexportMemberList(ModelMap map,

HttpServletRequest request,

HttpServletResponse response){

       List<Member> memberList = LocalJsonUtil.getListFromJson("json/members.json", Member.class);

  ExportParams params = new ExportParams("会员列表", "会员列表", ExcelType.XSSF);

   //对导出结果进行自定义处理

   MemberExcelDataHandler handler = new MemberExcelDataHandler();

    handler.setNeedHandlerFields(new String[]{"昵称"});

     params.setDataHandler(handler);

     map.put(NormalExcelConstants.DATA_LIST, memberList);

     map.put(NormalExcelConstants.CLASS, Member.class);

     map.put(NormalExcelConstants.PARAMS, params);

     map.put(NormalExcelConstants.FILE_NAME, "memberList");

      PoiBaseView.render(map, request, response, NormalExcelConstants.EASYPOI_EXCEL_VIEW);

    }

}

再次调用导出接口,我们可以发现昵称已经添加默认设置了。

体验了一波EasyPoi,它使用注解来操作Excel的方式确实非常好用。如果你想生成更为复杂的Excel的话,可以考虑下它的模板功能。

参考资料:

项目官网:https://gitee.com/lemur/easypoi

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 193,968评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,682评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,254评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,074评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,964评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,055评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,484评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,170评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,433评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,512评论 2 308
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,296评论 1 325
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,184评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,545评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,150评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,437评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,630评论 2 335

推荐阅读更多精彩内容