Excel导入导出

采用注解方式的导入导出,能让你在项目中更便捷的使用java

1、安装依赖

<dependency>
    <groupId>cn.gjing</groupId>
    <artifactId>tools-excel</artifactId>
    <version>1.1.4</version>
</dependency>

2、注解说明

一、@Excel

实体上使用,声明Excel与该实体存在映射,注解参数以下git

参数 描述
value Excel文件名,优先级于方法传入
type Excel文档类型,默认XLS
style 导出的Excel样式

二、@ExcelField

字段上使用,声明Excel的列表头会与该字段映射,注解参数以下github

参数 描述
value 列表头名字
pattern 如何字段是时间类型的,且须要格式转换,那么则必定要设置
width 这个列表头单元格的宽度,默认20 * 256,建议设置256的倍数

三、@DateValid

时间校验注解,使用在字段上,代表在Excel文件中这个列表头下指定行数的单元格会添加时间的校验,XLSX类型文档不支持,注解参数以下数组

参数 描述
validClass 校验器Class
boxLastRow 数据校验最多校验多少行,默认是正文第一行
pattern 校验的时间格式,默认yyyy-MM-dd
operatorType 操做类型,默认OperatorType.BETWEEN
expr1 表达式1,默认1970-01-01
expr2 表达式2,默认2999-01-01
showErrorBox 是否弹出错误框,默认true
showPromptBox 是否当即弹出,默认true
rank 提示框级别,默认Rank.WARING警告级别
errorTitle 错误框标题
errorContent 详细错误内容

四、@ExplicitValid

下拉框选值,使用在字段上,代表在Excel文件中这个列表头下指定行数的单元格会添加下拉框选项,注解参数以下app

参数 描述
validClass 校验器Class
combobox 范围值,数组类型
boxLastRow 数据校验最多校验多少行,默认是该列表头下的正文第一行
showErrorBox 是否弹出错误框,默认true
showPromptBox 是否当即弹出,默认true
rank 提示框级别,默认Rank.WARING警告级别
errorTitle 错误框标题
errorContent 详细错误内容

五、@NumericValid

数据类型校验,使用在字段上,代表在Excel文件中这个列表头下指定行数的单元格会添加数据类型的校验,注解参数以下ide

参数 描述
validClass 校验器Class
boxLastRow 数据校验最多校验多少行,默认是该列表头下的正文第一行
operatorType 操做类型,默认OperatorType.GREATER_OR_EQUAL
validType 校验类型,默认ValidType.INTEGER
expr1 表达式1,在表达式2前面,默认0
expr2 表达式2,在操做类型为BETWEENNOT_BETWEEN状况下必填
showErrorBox 是否弹出错误框,默认true
showPromptBox 是否当即弹出,默认true
rank 提示框级别,默认Rank.WARING警告级别
errorTitle 错误框标题
errorContent 详细错误内容

六、@ExcelEnumConvert

枚举转换器,使用在枚举类型的字段上,注解参数以下this

参数 描述
convert 实现了EnumConvert接口的类Class

3、使用说明

一、定义Excel对应的实体

只须要在实体类的字段上加上对应注解便可excel

@Data //这是lombok的注解,帮完成get、set等方法
@Excel("用户列表")
public class User {
    @ExcelField("用户Id")
    private Long id;

    @ExcelField("用户名")
    private String userName;

    @ExcelField(value = "建立时间",pattern = "yyyy-MM-dd")
    private Date createTime;
}

二、Excel导出

若是只是导出Excel模板,只须要在write()方法中传入null便可code

/**
 * @author Gjing
 **/
@RestController
public class UserController {
    @Resource
    private UserService userService;

    @GetMapping("/user")
    @ApiOperation(value = "导出用户")
    public void exportUser(HttpServletResponse response) {
        List<User> users = userService.userList();
        ExcelFactory.createWriter(User.class, response)
                .write(users)
                .flush();
    }
}

排除某些带了@ExcelField注解的字段,只需在createWriter()方法中ignores参数指定你要忽略的字段,字段名要和实体一致xml

/**
 * @author Gjing
 **/
@RestController
public class UserController {
    @Resource
    private UserService userService;

    @GetMapping("/user")
    @ApiOperation(value = "导出用户")
    public void exportUser(HttpServletResponse response) {
        List<User> users = userService.userList();
        ExcelFactory.createWriter(User.class, response,"id","createTime")
                .write(users)
                .flush();
    }
}

指定sheet导出,不指定的话会导出到默认名称的sheet中

/**
 * @author Gjing
 **/
@RestController
public class UserController {
    @Resource
    private UserService userService;

    @GetMapping("/user")
    @ApiOperation(value = "导出用户")
    public void exportUser(HttpServletResponse response) {
        List<User> users = userService.userList();
        ExcelFactory.createWriter(User.class, response)
                .write(users,"用户sheet")
                .flush();
    }
}

若是须要将不一样规则数据导入到不一样的sheet中或者是在同一个sheet中分层,能够屡次调用write()方法,若是不指定sheet的名称则会写入到默认名称的sheet

/**
 * @author Gjing
 **/
@RestController
public class UserController {
    @Resource
    private UserService userService;

    @GetMapping("/user")
    @ApiOperation(value = "导出用户")
    public void exportUser(HttpServletResponse response) {
        List<User> users = userService.userList();
        List<User> users2 = userService.userList();
        ExcelFactory.createWriter(User.class, response)
                .write(users)
                .write(users2,"sheet2")
                .flush();   
    }
}

指定大标题,该操做要在每次调用write()以前,仅对本次调用有效

/**
 * @author Gjing
 **/
@RestController
public class UserController {
    @Resource
    private UserService userService;

    @GetMapping("/user")
    @ApiOperation(value = "导出用户")
    public void exportUser(HttpServletResponse response) {
        List<User> users = userService.userList();
        ExcelFactory.createWriter(User.class, response)
                //指定大标题占用的行数和内容
                .bigTitle(() -> new BigTitle(3,"我是大标题"))
                .write(users)
                .flush();
    }
}

重置当前导出的Excel与实体的关联,该操做要在调用write()以前

/**
 * @author Gjing
 **/
@RestController
public class UserController {
    @Resource
    private UserService userService;
    @Resource
    private OrderService orderService;

    @GetMapping("/user")
    @ApiOperation(value = "导出用户")
    public void exportUser(HttpServletResponse response) {
        List<User> users = userService.userList();
        List<Order> orderList = orderService.orderList();
        ExcelFactory.createWriter(User.class, response)
                .write(users)
                //重置,若是要忽略某些字段不导出,在该方法 ignores 参数指定便可
                //注意点和上文介绍的导出一致
                .resetExcelClass(Order.class)
                .write(orderList)
                .flush();
    }
}

在整个链式调用完毕后,必定要在最后调用flush()方法,不然数据不会写入到Excel文件中

三、Excel导入

经过get()方法获取导入的数据,该方法为最终操做,整个导入会结束

/**
 * @author Gjing
 **/
@RestController
public class UserController {
    @Resource
    private UserService userSerivce;

    @PostMapping("/user_import")
    @ApiOperation("导入")
    public ResponseEntity userImport(MultipartFile file) throws IOException {
        List<User> users = ExcelFactory.createReader(file.getInputStream(), User.class)
                //不指定sheet名称,会去读默认名称的sheet
                .read()
                .get();
        userService.saveUserList(users);
        return ResponseEntity.ok("导入成功");
    }
}

不少时候咱们数据太多而写在了不一样的sheet中,这时可能须要分开读取每一个sheet,使用上面的get()方法获取导入的结果确定是不行的,这时能够经过建立监听者去监听每次导入的结果并执行对应的操做

/**
 * @author Gjing
 **/
@RestController
public class UserController {
    @Resource
    private UserService userSerivce;

    @PostMapping("/user_import")
    @ApiOperation("导入")
    public ResponseEntity userImport(MultipartFile file) throws IOException {
        ExcelFactory.createReader(file.getInputStream(), User.class)
                .read()
                .listener(e -> userService.saveUserList(e))
                .read("sheet2")
                .listener(e -> userService.saveUserList(e));
        return ResponseEntity.ok("导入成功");
    }
}

导入的Excel存在大标题的话,须要指定列表头开始的下标,对应Excel文件列表头那一行左边的数字序号,该操做要在调用read()前进行

/**
 * @author Gjing
 **/
@RestController
public class UserController {
    @Resource
    private UserService userSerivce;

    @PostMapping("/user_import")
    @ApiOperation("导入")
    public ResponseEntity userImport(MultipartFile file) throws IOException {
        List<User> users = ExcelFactory.createReader(file.getInputStream(), User.class)
                .headerIndex(2)
                .read()
                .get();
        userService.saveUserList(users);
        return ResponseEntity.ok("导入成功");
    }
}

限制读取行数,该行数为你要截止读取到Excel文件的哪一行(包括本行),该操做要在调用read()前进行

/**
 * @author Gjing
 **/
@RestController
public class UserController {
    @Resource
    private UserService userSerivce;

    @PostMapping("/user_import")
    @ApiOperation("导入")
    public ResponseEntity userImport(MultipartFile file) throws IOException {
        List<User> users = ExcelFactory.createReader(file.getInputStream(), User.class)
                .endIndex(5)
                .read()
                .get();
        userService.saveUserList(users);
        return ResponseEntity.ok("导入成功");
    }
}

四、枚举转换器

实体存在枚举类型的字段,在导入导出时须要指定枚举转换器,实现EnumConvert接口并重写其中方法,导出会显示你指定的内容,导入会转为指定的枚举

/**
 * 性别
 *
 * @author Gjing
 **/
@Getter
public enum GenderEnum {
    MAN(1, "男"),
    WOMAN(2, "女");
    private int type;
    private String desc;

    GenderEnum(int type, String desc) {
        this.type = type;
        this.desc = desc;
    }

    /**
     * excel导入的时候转换成枚举用的
     *
     * @param s desc
     * @return GenderEnum
     */
    public static GenderEnum of(String s) {
        for (GenderEnum genderEnum : GenderEnum.values()) {
            if (genderEnum.getDesc().equals(s)) {
                return genderEnum;
            }
        }
        throw new NullPointerException("没找到你的枚举");
    }
    /**
     * 自定义枚举转换器
     */
    public static class MyExcelEnumConvert implements EnumConvert<GenderEnum, String> {
        @Override
        public GenderEnum toEntityAttribute(String s) {
            return of(s);
        }

        @Override
        public String toExcelAttribute(GenderEnum genderEnum) {
            return genderEnum.desc;
        }
    }
}

字段使用注解并指定你的转换类

@Excel("用户列表")
public class User {

    @ExcelField("性别")
    @ExcelEnumConvert(convert = GenderEnum.MyExcelEnumConvert.class)
    private GenderEnum genderEnum;
}

五、数据校验

在导出模板时,有时候须要让用户填写指定规则的内容,这时能够进行数据格式校验

@Excel("用户列表")
public class User {
    @ExcelField("用户Id")
    private Long id;

    @ExcelField("用户名")
    //在当前列表头下方的十行单元格进行数据校验,必须输入长度大于等于3的文本
    @NumericValid(validationType = TEXT_LENGTH, expr1 = "3", operatorType = GREATER_OR_EQUAL,boxLastRow = 10)
    private String userName;

    @ExcelField("性别")
    //指定该列表头下的第一行单元格只能选取这两个值
    @ExplicitValid(combobox = {"男","女"})
    @ExcelEnumConvert(convert = GenderEnum.MyExcelEnumConvert.class)
    private GenderEnum genderEnum;

    @ExcelField(value = "建立时间",pattern = "yyyy-MM-dd")
    //在当前列表头下方的第一行单元格中,时间只能输入在2019-10-11至2019-10-13范围的时间
    @DateValid(expr1 = "2019-10-11",expr2 = "2019-10-13")
    private Date createTime;
}

4、拓展

以前讲解的使用的一些功能都是采起默认实现的,有时候有点本身的想法,也能够进行自定义其中的功能

一、自定义Excel样式

实现ExcelStyle接口,里面有设置大标题、正文、列表头样式的三个方法,选择你要自定义的方法进行重写便可,如下演示了自定义大标题样式

/**
 * 本身定义的样式
 * @author Gjing
 **/
public class MyExcelStyle implements ExcelStyle {

    @Override
    public CellStyle setTitleStyle(CellStyle cellStyle) {
        cellStyle.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.index);
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        cellStyle.setWrapText(true);
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        return cellStyle;
    }
}

在对应实体@Excel注解指定你自定义的样式类

@Excel(value = "用户列表",style = MyExcelStyle.class)
public class User {
    @ExcelField("用户Id")
    private Long id;

    @ExcelField("用户名")
    private String userName;

    @ExcelField(value = "建立时间",pattern = "yyyy-MM-dd")
    private Date createTime;
}

二、自定义校验注解的逻辑

经过上文的介绍,知道一共有三种校验的注解,这三个注解分别均可以自定义实现逻辑,实现ExcelValidation接口,并选择你须要自定义哪些注解的逻辑

/**
 * 本身实现校验逻辑
 * @author Gjing
 **/
public class MyValid implements ExcelValidation {
    @Override
    public void valid(DateValid dateValid, Sheet sheet, int firstRow, int firstCol, int lastCol) {

    }

    @Override
    public void valid(NumericValid numericValid, Sheet sheet, int firstRow, int firstCol, int lastCol) {

    }

    @Override
    public void valid(ExplicitValid explicitValid, Workbook workbook, Sheet sheet, int firstRow, int firstCol, int lastCol) {

    }
}

修改你实体类中对应注解的默认处理类validClass,没有指定的注解仍是会走默认处理

@Excel("用户列表")
public class User {
    @ExcelField("用户Id")
    private Long id;

    @ExcelField("用户名")
    @NumericValid(validClass = MyValid.class,validationType = TEXT_LENGTH, expr1 = "3", operatorType = GREATER_OR_EQUAL,boxLastRow = 10)
    private String userName;

    @ExcelField(value = "建立时间",pattern = "yyyy-MM-dd")
    //在当前列表头下方的第一行单元格中,时间只能输入在2019-10-11至2019-10-13范围的时间
    @DateValid(expr1 = "2019-10-11",expr2 = "2019-10-13")
    private Date createTime;
}

三、自定义导出处理器

自定义导出的核心处理器,需实现ExcelWriterResolver接口并重写其中的方法

/**
 * 自定义导出处理器
 * @author Gjing
 **/
public class MyWriteResolver implements ExcelWriterResolver {
    @Override
    public void write(List<?> list, Workbook workbook, String s, List<Field> list1, MetaStyle metaStyle, BigTitle bigTitle) {

    }

    @Override
    public void flush(HttpServletResponse httpServletResponse, String s) {

    }
}

在导出时重置处理器,该方法要在你全部链式操做以前调用

/**
 * @author Gjing
 **/
@RestController
@Api(tags = "用户")
public class UserController {
    @Resource
    private UserService userService;

    @GetMapping("/user")
    @ApiOperation(value = "使用自定义的导出处理器导出用户模板")
    public void exportUser(HttpServletResponse response) {
        ExcelFactory.createWriter(User.class, response)
                //重置处理器
                .resetResolver(MyWriteResolver::new)
                .write(null)
                .flush();
    }
}

四、自定义导入处理器

自定义导入处理器,需实现ExcelReaderResolver接口并重写其中的方法

/**
 * 自定义导入处理器
 * @author Gjing
 **/
public class MyReaderResolver implements ExcelReaderResolver {
    @Override
    public void read(InputStream inputStream, Class<?> aClass, Listener<List<Object>> listener, int i, int i1, String s) {

    }
}

在导出的方法中重置处理器,该操做要在全部链式操做前调用

/**
 * @author Gjing
 **/
@RestController
public class UserController {
    @Resource
    private UserService userSerivce;

    @PostMapping("/user_import")
    @ApiOperation("导入")
    public ResponseEntity userImport(MultipartFile file) throws IOException {
        ExcelFactory.createReader(file.getInputStream(), User.class)
                .resetResolver(MyReaderResolver::new)
                .read()
                .listener(e -> userService.saveUserList(e))
        return ResponseEntity.ok("导入成功");
    }
}

源代码地址:tools-excel
Demo地址:excel-demo

相关文章
相关标签/搜索