SpringBoot实现Excel导入导出

SpringBoot实现Excel导入导出

在我们平时工作中经常会遇到要操作Excel的功能,比如导出个用户信息或者订单信息的Excel报表。你肯定听说过

POI这个东西,可以实现。但是POI实现的API确实很麻烦,它需要写那种逐行解析的代码(类似Xml解析)。今天

给大家推荐一款非常好用的Excel导入导出工具EasyPoi,希望对大家有所帮助!

1、EasyPoi简介

用惯了SpringBoot的朋友估计会想到,有没有什么办法可以直接定义好需要导出的数据对象,然后添加几个注

解,直接自动实现Excel导入导出功能?

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

EasyPoi的目标不是替代POI,而是让一个不懂导入导出的人也能快速使用POI完成Excel的各种操作,而不是看很

多API才可以完成这样的工作。

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

2、集成

在SpringBoot中集成EasyPoi非常简单,只需添加easypoi-spring-boot-starter依赖即可,真正的开箱即

用!

2.1 pom依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.5.6</version>
		<relativePath/>
	</parent>
	<groupId>com.easypoi</groupId>
	<artifactId>spring-boot-easypoi</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>spring-boot-easypoi</name>
	<description>pringBoot实现Excel导入导出</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>cn.afterturn</groupId>
			<artifactId>easypoi-spring-boot-starter</artifactId>
			<version>4.4.0</version>
		</dependency>

		<!--springfox swagger官方Starter-->
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-boot-starter</artifactId>
			<version>3.0.0</version>
		</dependency>

        <!--lombok依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

		<!--SpringBoot通用依赖模块-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>

	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

2.2 结果集封装

package com.easypoi.common.api;

/**
 * 封装API的错误码
 */
public interface IErrorCode {
    
    long getCode();

    String getMessage();
}
package com.easypoi.common.api;

/**
 * 枚举了一些常用API操作码
 */
public enum ResultCode implements IErrorCode {
    
    SUCCESS(200, "操作成功"),
    FAILED(500, "操作失败"),
    VALIDATE_FAILED(404, "参数检验失败"),
    UNAUTHORIZED(401, "暂未登录或token已经过期"),
    FORBIDDEN(403, "没有相关权限");
    private long code;
    private String message;

    private ResultCode(long code, String message) {
        this.code = code;
        this.message = message;
    }

    public long getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }
}
package com.easypoi.common.api;

/**
 * 通用返回对象
 */
public class CommonResult<T> {
    private long code;
    private String message;
    private T data;

    protected CommonResult() {
    }

    protected CommonResult(long code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }

    /**
     * 成功返回结果
     *
     * @param data 获取的数据
     */
    public static <T> CommonResult<T> success(T data) {
        return new CommonResult<T>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);
    }

    /**
     * 成功返回结果
     *
     * @param data    获取的数据
     * @param message 提示信息
     */
    public static <T> CommonResult<T> success(T data, String message) {
        return new CommonResult<T>(ResultCode.SUCCESS.getCode(), message, data);
    }

    /**
     * 失败返回结果
     *
     * @param errorCode 错误码
     */
    public static <T> CommonResult<T> failed(IErrorCode errorCode) {
        return new CommonResult<T>(errorCode.getCode(), errorCode.getMessage(), null);
    }

    /**
     * 失败返回结果
     *
     * @param message 提示信息
     */
    public static <T> CommonResult<T> failed(String message) {
        return new CommonResult<T>(ResultCode.FAILED.getCode(), message, null);
    }

    /**
     * 失败返回结果
     */
    public static <T> CommonResult<T> failed() {
        return failed(ResultCode.FAILED);
    }

    /**
     * 参数验证失败返回结果
     */
    public static <T> CommonResult<T> validateFailed() {
        return failed(ResultCode.VALIDATE_FAILED);
    }

    /**
     * 参数验证失败返回结果
     *
     * @param message 提示信息
     */
    public static <T> CommonResult<T> validateFailed(String message) {
        return new CommonResult<T>(ResultCode.VALIDATE_FAILED.getCode(), message, null);
    }

    /**
     * 未登录返回结果
     */
    public static <T> CommonResult<T> unauthorized(T data) {
        return new CommonResult<T>(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data);
    }

    /**
     * 未授权返回结果
     */
    public static <T> CommonResult<T> forbidden(T data) {
        return new CommonResult<T>(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data);
    }

    public long getCode() {
        return code;
    }

    public void setCode(long code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

2.3 Swagger配置

package com.easypoi.config;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * Swagger2API文档的配置
 */
@Configuration
@EnableSwagger2
public class Swagger2Config {
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                //为当前包下controller生成API文档
                .apis(RequestHandlerSelectors.basePackage("com.easypoi.controller"))
                //为有@Api注解的Controller生成API文档
                .apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
                //为有@ApiOperation注解的方法生成API文档
                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("SwaggerUI演示")
                .description("mall-tiny")
                .contact(new Contact("macro", null, null))
                .version("1.0")
                .build();
    }
}

2.4 启动类

package com.easypoi;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootEasypoiApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootEasypoiApplication.class, args);
    }

}

2.5 配置文件

server:
  port: 8088

springfox:
  documentation:
    enabled: true

2.6 实体类

package com.easypoi.domain;

import cn.afterturn.easypoi.excel.annotation.Excel;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.util.Date;

/**
 * 购物会员
 */
@Data
@EqualsAndHashCode(callSuper = false)
public class Member {
    @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;
}
package com.easypoi.domain;

import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.annotation.ExcelCollection;
import cn.afterturn.easypoi.excel.annotation.ExcelEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.util.Date;
import java.util.List;

/**
 * 订单
 */
@Data
@EqualsAndHashCode(callSuper = false)
public class Order {

    @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;
}
package com.easypoi.domain;

import cn.afterturn.easypoi.excel.annotation.Excel;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.math.BigDecimal;

/**
 * 商品
 */
@Data
@EqualsAndHashCode(callSuper = false)
public class Product {
    @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;
}

2.7 工具类

package com.easypoi.util;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;

import java.nio.charset.Charset;
import java.util.List;

/**
 * 从本地获取JSON数据的工具类
 */
public class LocalJsonUtil {

    /**
     * 从指定路径获取JSON并转换为List
     *
     * @param path        json文件路径
     * @param elementType List元素类型
     */
    public static <T> List<T> getListFromJson(String path, Class<T> elementType) {
        ClassPathResource resource = new ClassPathResource(path);
        String jsonStr = IoUtil.read(resource.getStream(), Charset.forName("UTF-8"));
        JSONArray jsonArray = new JSONArray(jsonStr);
        return JSONUtil.toList(jsonArray, elementType);
    }
}

LocalJsonUtil工具类,可以直接从resources目录下获取JSON数据并转化为对象。

2.8 字段处理器

package com.easypoi.handler;

import cn.afterturn.easypoi.handler.impl.ExcelDataHandlerDefaultImpl;
import cn.hutool.core.util.StrUtil;
import com.easypoi.domain.Member;

/**
 * 自定义字段处理
 */
public class MemberExcelDataHandler extends ExcelDataHandlerDefaultImpl<Member> {

    @Override
    public Object exportHandler(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
    public Object importHandler(Member obj, String name, Object value) {
        return super.importHandler(obj, name, value);
    }
}

2.9 处理器

package com.easypoi.controller;

import cn.afterturn.easypoi.entity.vo.NormalExcelConstants;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.view.PoiBaseView;
import com.easypoi.common.api.CommonResult;
import com.easypoi.domain.Member;
import com.easypoi.domain.Order;
import com.easypoi.domain.Product;
import com.easypoi.handler.MemberExcelDataHandler;
import com.easypoi.util.LocalJsonUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * EasyPoi导入导出测试Controller
 */
@Controller
@Api(tags = "EasyPoiController", description = "EasyPoi导入导出测试")
@RequestMapping("/easyPoi")
public class EasyPoiController {

    @ApiOperation(value = "导出会员列表Excel")
    @RequestMapping(value = "/exportMemberList", method = RequestMethod.GET)
    public void exportMemberList(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);
    }

    @ApiOperation("从Excel导入会员列表")
    @RequestMapping(value = "/importMemberList", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult importMemberList(@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("导入失败!");
        }
    }

    @ApiOperation(value = "导出订单列表Excel")
    @RequestMapping(value = "/exportOrderList", method = RequestMethod.GET)
    public void exportOrderList(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);
    }

    private List<Order> getOrderList() {
        List<Order> orderList = LocalJsonUtil.getListFromJson("json/orders.json", Order.class);
        List<Product> productList = LocalJsonUtil.getListFromJson("json/products.json", Product.class);
        List<Member> memberList = LocalJsonUtil.getListFromJson("json/members.json", Member.class);
        for (int i = 0; i < orderList.size(); i++) {
            Order order = orderList.get(i);
            order.setMember(memberList.get(i));
            order.setProductList(productList);
        }
        return orderList;
    }
}

2.10 测试

接下来介绍下EasyPoi的使用,以会员信息和订单信息的导入导出为例,分别实现下简单的单表导出和具有关联信

息的复杂导出。

2.10.1 简单导出

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

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

  • 在此我们就可以看到EasyPoi的核心注解@Excel,通过在对象上添加@Excel注解,可以将对象信息直接导出

    到Excel中去,下面对注解中的属性做个介绍;

    • name:Excel中的列名;

    • width:指定列的宽度;

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

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

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

    • replace:对属性进行替换;

    • suffix:对数据添加后缀。

  • 接下来我们在Controller中添加一个接口,用于导出会员列表到Excel,导出的member.json的内容:

[
  {
    "id": 1,
    "username": "admin",
    "password": null,
    "nickname": "Admin",
    "birthday": "1994-12-31",
    "phone": "18790000000",
    "icon": null,
    "gender": 0
  },
  {
    "id": 2,
    "username": "macro",
    "password": null,
    "nickname": "Macro",
    "birthday": "1995-01-31",
    "phone": "18791000000",
    "icon": null,
    "gender": 0
  },
  {
    "id": 3,
    "username": "andy",
    "password": null,
    "nickname": "Andy",
    "birthday": "1995-02-28",
    "phone": "18792000000",
    "icon": null,
    "gender": 1
  },
  {
    "id": 4,
    "username": "ruby",
    "password": null,
    "nickname": "Ruby",
    "birthday": "1995-03-31",
    "phone": "18793000000",
    "icon": null,
    "gender": 1
  },
  {
    "id": 5,
    "username": "tom",
    "password": null,
    "nickname": "",
    "birthday": "1995-03-31",
    "phone": "18793000000",
    "icon": null,
    "gender": 1
  }
]

运行项目,直接通过Swagger访问接口,注意在Swagger中访问接口无法直接下载,需要点击返回结果中的下载按

钮才行,访问地址:http://localhost:8088/swagger-ui/

在这里插入图片描述

在这里插入图片描述

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

memberList.xlsx文件内容:

在这里插入图片描述

2.10.2 简单导入

导入功能实现起来也非常简单,下面以会员信息列表的导入为例。

  • 在Controller中添加会员信息导入的接口,这里需要注意的是使用@RequestPart注解修饰文件上传参数,否

    则在Swagger中就没法显示上传按钮了;

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

在这里插入图片描述

得到的结果:

{
  "code": 200,
  "message": "操作成功",
  "data": [
    {
      "id": 1,
      "username": "admin",
      "password": null,
      "nickname": "Admin",
      "birthday": "1994-12-30T16:00:00.000+00:00",
      "phone": "187****0000",
      "icon": null,
      "gender": 0
    },
    {
      "id": 2,
      "username": "macro",
      "password": null,
      "nickname": "Macro",
      "birthday": "1995-01-30T16:00:00.000+00:00",
      "phone": "187****0000",
      "icon": null,
      "gender": 0
    },
    {
      "id": 3,
      "username": "andy",
      "password": null,
      "nickname": "Andy",
      "birthday": "1995-02-27T16:00:00.000+00:00",
      "phone": "187****0000",
      "icon": null,
      "gender": 1
    },
    {
      "id": 4,
      "username": "ruby",
      "password": null,
      "nickname": "Ruby",
      "birthday": "1995-03-30T16:00:00.000+00:00",
      "phone": "187****0000",
      "icon": null,
      "gender": 1
    },
    {
      "id": 5,
      "username": "tom",
      "password": null,
      "nickname": "暂未设置",
      "birthday": "1995-03-30T16:00:00.000+00:00",
      "phone": "187****0000",
      "icon": null,
      "gender": 1
    }
  ]
}
2.10.3 复杂导出

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

实现下!

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

  • 然后添加订单对象Order,订单和会员是一对一关系,使用@ExcelEntity注解表示,订单和商品是一对多关

    系,使用@ExcelCollection注解表示,Order就是我们需要导出的嵌套订单数据;

  • 接下来在Controller中添加导出订单列表的接口,由于有些会员信息我们不需要导出,可以调用

    ExportParams中的setExclusions方法排除掉;

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

products.json的内容:

[
  {
    "id": 1,
    "productSn": "7437788",
    "name": "小米8",
    "subTitle": "全面屏游戏智能手机 6GB+64GB 黑色 全网通4G 双卡双待",
    "brandName": "小米",
    "price": 2699,
    "count": 1
  },
  {
    "id": 2,
    "productSn": "7437789",
    "name": "红米5A",
    "subTitle": "全网通版 3GB+32GB 香槟金 移动联通电信4G手机 双卡双待",
    "brandName": "小米",
    "price": 649,
    "count": 1
  },
  {
    "id": 3,
    "productSn": "7437799",
    "name": "Apple iPhone 8 Plus",
    "subTitle": "64GB 红色特别版 移动联通电信4G手机",
    "brandName": "苹果",
    "price": 5499,
    "count": 1
  }
]

orders.json的内容:

[
  {
    "id": 1,
    "orderSn": "201809150101000001",
    "createTime": "2021-10-10 17:02:28",
    "receiverAddress": "广东省深圳市"
  },
  {
    "id": 1,
    "orderSn": "201809150101000002",
    "createTime": "2021-10-11 17:02:28",
    "receiverAddress": "江苏省南京市"
  },
  {
    "id": 1,
    "orderSn": "201809150101000003",
    "createTime": "2021-10-12 17:02:28",
    "receiverAddress": "江苏省苏州市"
  }
]

在这里插入图片描述

  • 下载完成后,查看下文件,EasyPoi导出复杂的Excel也是很简单的!

在这里插入图片描述

2.10.4 自定义处理

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

们添加下暂未设置信息。

  • 我们需要添加一个处理器继承默认的ExcelDataHandlerDefaultImpl类,然后在exportHandler方法中实

    现自定义处理逻辑;

  • 然后修改Controller中的接口,调用MemberExcelDataHandler处理器的setNeedHandlerFields设置需要

    自定义处理的字段,并调用ExportParamssetDataHandler设置自定义处理器;

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

上面的代码已经是自定义处理过的。

//对导出结果进行自定义处理
MemberExcelDataHandler handler = new MemberExcelDataHandler();
handler.setNeedHandlerFields(new String[]{"昵称"});
params.setDataHandler(handler);

3、总结

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

虑下它的模板功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/128994.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MicroPython ESP32 RTC功能使用介绍

MicroPython ESP32 RTC功能使用介绍 &#x1f4cc;Micropython esp32官方文档介绍&#xff1a;https://docs.micropython.org/en/latest/esp32/quickref.html#real-time-clock-rtc&#x1f516;本示例基于Thonny平台开发。&#x1f33f;使用ESP32S3开发板测试。✨所使用的固件版…

2023双十一:实体门店闯入,第二战场全面开战

“闺女&#xff0c;吃饺子了吗&#xff1f;”11月8日&#xff0c;立冬&#xff0c;忙碌一天的陈曦回家路上接到母亲电话&#xff0c;才想起来家里冷冻水饺没了&#xff0c;又不想再去超市&#xff0c;直接打开美团买菜买了两袋&#xff0c;回家就煮了吃。当然&#xff0c;最终她…

多级缓存之实现多级缓存

多级缓存的实现离不开Nginx编程&#xff0c;而Nginx编程又离不开OpenResty。 1. OpenResty快速入门 我们希望达到的多级缓存架构如图&#xff1a; 其中&#xff1a; windows上的nginx用来做反向代理服务&#xff0c;将前端的查询商品的ajax请求代理到OpenResty集群 OpenRest…

最详细的LightGBM参数介绍与深入分析

前言 我使用LightGBM有一段时间了&#xff0c;它一直是我处理大多数表格数据问题的首选算法。它有很多强大的功能&#xff0c;如果你还没有看过的话&#xff0c;我建议你去了解一下。 但我一直对了解哪些参数对性能影响最大&#xff0c;以及如何调整LightGBM参数以发挥最大作用…

Python算法:动态规划解决0-1背包问题

动态规划&#xff08;Dynamic Programming&#xff0c;简称DP&#xff09;是一种在数学、计算机科学和经济学中使用的&#xff0c;通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划常常适用于有重叠子问题和最优子结构性质的问题&#xff0c;它能够将问题…

Android修行手册 - POI操作Excel常用样式(字体,背景,颜色,Style)

点击跳转>Unity3D特效百例点击跳转>案例项目实战源码点击跳转>游戏脚本-辅助自动化点击跳转>Android控件全解手册点击跳转>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&…

Unity中Shader雾效的原理

文章目录 前言一、我们先看一下现实中的雾二、雾效的混合公式最终的颜色 lerp(雾效颜色&#xff0c;物体颜色&#xff0c;雾效混合因子) 三、雾效的衰减1、FOG_LINEAR&#xff08;线性雾衰减&#xff09;2、FOG_EXP(指数雾衰减1)3、FOG_EXP(指数雾衰减2) 前言 Unity中Shader雾…

ESP32 下蓝牙播放音乐

之前发过一贴&#xff1a; esp32 下蓝牙播放音乐歌词的获得_esp32 蓝牙音频-CSDN博客 说的是esp32 蓝牙接收音频流同步获得歌词的方案&#xff0c;但是有个很核心的内容由于硬件原因没有谈及&#xff0c;就是播放音乐。 这几天被抖音上各种水桶卡顿刺激了&#xff0c;经过一…

初识Linux:目录路径

目录 提示&#xff1a;以下指令均在Xshell 7 中进行 一、基本指令&#xff1a; 二、文件 文件内容文件属性 三、ls 指令拓展 1、 ls -l &#xff1a; 2、ls -la&#xff1a; 3、ls [目录名] &#xff1a; 4、ls -ld [目录名]&#xff1a; 四、Linux中的文件和…

【获取cookie的真实到期时间】

获取cookie的真实到期时间 from datetime import datetime print(datetime.fromtimestamp(1734148606))

Java中的可变参数详解与最佳实践

Java中的可变参数详解与最佳实践 摘要引言可变参数的基本概念什么是可变参数&#xff1f;可变参数的语法 可变参数的使用场景与最佳实践何时使用可变参数&#xff1f; 最佳实践&#xff1a;谨慎使用可变参数灵活性 vs. 清晰性避免滥用的情况1. 类型安全问题2. 过多的参数3. 易混…

基于Docker容器DevOps应用方案

文章目录 基于docker容器DevOps应用方案环境基础配置1.所有主机永久关闭防火墙和selinux2.配置yum源3.docker的安装教程 配置主机名与IP地址解析部署gitlab.server主机1.安装gitlab2.配置gitlab3.破解管理员密码4.验证web页面 部署jenkins.server主机1.部署tomcat2.安装jenkins…

[autojs]用户界面GUI编程

用户界面: UI视图: View attr(name, value)attr(name)whidgravitylayout_gravitymarginmarginLeftmarginRightmarginTopmarginBottompaddingpaddingLeftpaddingRightpaddingToppaddingBottombgalphaforegroundminHeightminWidthvisibilityrotationtransformPivotXtransformPivo…

安卓编译命令mm和mmm的区别(mm编译当前工作目录,mmm可编译指定目录)

文章目录 1. mm示例 2. mmm示例 注意 在Android操作系统的源代码编译过程中&#xff0c; mm和 mmm是两个用于构建部分代码的常用命令。它们都属于Android build system提供的命令集合&#xff0c;但用途略有不同&#xff1a; 1. mm mm&#xff08;make module&#xff09;命…

Linux C语言进阶-D15递归函数和函数指针

递归函数 指一个函数的函数体中直接或间接调用了该函数本身 执行过程分为两个过程&#xff1a; 递推过程&#xff1a;从原问题出发&#xff0c;按递归公式递推从未知到已知&#xff0c;最终达到递推终止条件 回归阶段&#xff1a;按递归终止条件求出结果&#xff0c;逆向逐步…

无线城市WiFi解决方案【完整Word】

wx供重浩&#xff1a;创享日记 获取完整无水印高清Word版 文章目录 第1章 项目背景1.1“无线城市”的定义1.2 国内外“无线城市”发展概况1.3 典型案例分析1.4 建设无线城市的必要性1.5 无线城市能为政府带来的价值 第2章 项目需求分析2.1 无线城市的现状分析2.2 无线城市的总体…

Apache Airflow (三) :Airflow WebUI操作介绍

&#x1f3e1; 个人主页&#xff1a;IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 &#x1f6a9; 私聊博主&#xff1a;加入大数据技术讨论群聊&#xff0c;获取更多大数据资料。 &#x1f514; 博主个人B栈地址&#xff1a;豹哥教你大数据的个人空间-豹…

【Hadoop实战】Hadoop指标系统V2分析

Hadoop指标系统V2分析 文章目录 Hadoop指标系统V2分析架构主要组成部分根据图表解释数据流向指标过滤JMX的应用开启指标系统的组件指标项说明 使用HTTP&#xff08;JMXJsonServlet&#xff09;获取指标接口调用方式GET查询的逻辑数据的来源&#xff0c;以及更新的原理 架构 在…

chrome 的vue3的开发者devtool不起作用

问题&#xff1a; 刚刚vue2升级到vue3&#xff0c;旧的devtool识别不了vue3数据。 原因&#xff1a; devtool版本过低。升级到最新。 解决&#xff1a; 去github下载vuetool项目代码&#xff1a; GitHub - vuejs/devtools: ⚙️ Browser devtools extension for debugging…

C#基于inpoutx64读写ECRAM硬件信息

inpoutx64.dll分享路径&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rOt0xtt9EcsrFQtf7S91ag 提取码&#xff1a;7om1 1.InpOutManager&#xff1a; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServi…