excel中怎样自动排序号(EasyExcel,让excel导入导出更加简单)

wufei123 发布于 2024-01-03 阅读(242)

点击上方蓝色字体,选择“标星公众号”优质文章,第一时间送达在做excel导入导出的时候,发现项目中封装的工具类及其难用,于是去gitHub上找了一些相关的框架,最终选定了EasyExcel之前早有听闻该框架,但是一直没有去了解,这次借此学习一波,提高以后的工作效率。

实际使用中,发现是真的很easy,大部分api通过名称就能知道大致意思,这点做的很nice参考文档,大部分场景的需求基本都能够满足GitHub上的官方说明

快速开始maven仓库地址    com.alibaba    easyexcel    2.1.2

导入如下图excel表格:

建立导入对应实体类@Datapublic class ReqCustomerDailyImport {    /**     * 客户名称     */    @ExcelProperty(index = 0)

    private String customerName;    /**     * MIS编码     */    @ExcelProperty(index = 1)    private String misCode;

    /**     * 月度滚动额     */    @ExcelProperty(index = 3)    private BigDecimal monthlyQuota;    /**     * 最新应收账款余额

     */    @ExcelProperty(index = 4)    private BigDecimal accountReceivableQuota;    /**     * 本月利率(年化)

     */    @ExcelProperty(index = 5)    private BigDecimal dailyInterestRate;}Controller代码@PostMapping(

"/import")public void importCustomerDaily(@RequestParam MultipartFile file) throws IOException {    InputStream inputStream = file.getInputStream();

    List reqCustomerDailyImports = EasyExcel.read(inputStream)            .head(ReqCustomerDailyImport.class)

            // 设置sheet,默认读取第一个            .sheet()            // 设置标题所在行数            .headRowNumber(2)

            .doReadSync();}运行结果

可以看出只需要在实体对象使用@ExcelProperty注解,读取时指定该class,即可读取,并且自动过滤了空行,对于excel的读取及其简单不过此时发现一个问题,这样我如果要校验字段该怎么办?要将字段类型转换成另外一个类型呢?。

不必担心,我们可以想到的问题,作者肯定也考虑到了,下面来一个Demo代码如下List reqCustomerDailyImports = EasyExcel.read(inputStream)

            // 这个转换是成全局的, 所有java为string,excel为string的都会用这个转换器            // 如果就想单个字段使用请使用@ExcelProperty 指定converter。

            .registerConverter(new StringConverter())            // 注册监听器,可以在这里校验字段            .registerReadListener(new CustomerDailyImportListener())

            .head(ReqCustomerDailyImport.class)            .sheet()            .headRowNumber(2)            .doReadSync();

}监听器public class CustomerDailyImportListener extends AnalysisEventListener {    List misCodes = Lists.newArrayList();

    /**     * 每解析一行,回调该方法     * @param data     * @param context     */    @Override    public void invoke(Object data, AnalysisContext context) {

        String misCode = ((ReqCustomerDailyImport) data).getMisCode();if (StringUtils.isEmpty(misCode)) {

            throw new RuntimeException(String.format("第%s行MIS编码为空,请核实", context.readRowHolder().getRowIndex() + 1));

        }if (misCodes.contains(misCodes)) {            throw new RuntimeException(String.format("第%s行MIS编码已重复,请核实"

, context.readRowHolder().getRowIndex() + 1));        } else {            misCodes.add(misCode);        }

    }    /**     * 出现异常回调     * @param exception     * @param context     * @throws Exception     */    @Override

    public void onException(Exception exception, AnalysisContext context) throws Exception {        // ExcelDataConvertException:当数据转换异常的时候,会抛出该异常,此处可以得知第几行,第几列的数据

if (exception instanceof ExcelDataConvertException) {            Integer columnIndex = ((ExcelDataConvertException) exception).getColumnIndex() + 1;

            Integer rowIndex = ((ExcelDataConvertException) exception).getRowIndex() + 1;            String message = 

"第" + rowIndex + "行,第" + columnIndex + "列" + "数据格式有误,请核实";            throw new RuntimeException(message);

        } elseif (exception instanceof RuntimeException) {            throw exception;        } else {

            super.onException(exception, context);        }    }    /**     * 解析完全部回调     * @param context

     */    @Override    public void doAfterAllAnalysed(AnalysisContext context) {        misCodes.clear();

    }}转换器public class StringConverter implements Converter {    @Override    public Class supportJavaTypeKey

() {return String.class;    }    @Override    public CellDataTypeEnum supportExcelTypeKey() {return CellDataTypeEnum.STRING;

    }    /**     * 将excel对象转成Java对象,这里读的时候会调用     *     * @param cellData            NotNull     * @param contentProperty     Nullable

     * @param globalConfiguration NotNull     * @return     */    @Override    public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,

                                    GlobalConfiguration globalConfiguration) {return"自定义:" + cellData.getStringValue();

    }    /**     * 将Java对象转成String对象,写出的时候调用     *     * @param value     * @param contentProperty     * @param globalConfiguration

     * @return     */    @Override    public CellData convertToExcelData(String value, ExcelContentProperty contentProperty,

                                       GlobalConfiguration globalConfiguration) {return new CellData(value);

    }}可以看出注册了一个监听器:CustomerDailyImportListener,还注册了一个转换器:StringConverter流程为:框架读取一行数据,先执行转换器,当一行数据转换完成,执行监听器的回调方法,如果转换的过程中,出现转换异常,也会回调监听器中的onException方法。

因此,可以在监听器中校验数据,在转换器中转换数据类型或者格式运行结果

修改一下表格,测试校验是否生效

再次导入,查看运行结果

导入相关常用API注解ExcelProperty 指定当前字段对应excel中的那一列可以根据名字或者Index去匹配当然也可以不写,默认第一个字段就是index=0,以此类推千万注意,要么全部不写,要么全部用index,要么全部用名字去匹配。

千万别三个混着用,除非你非常了解源代码中三个混着用怎么去排序的ExcelIgnore 默认所有字段都会和excel去匹配,加了这个注解会忽略该字段DateTimeFormat 日期转换,用String去接收excel日期格式的数据会调用这个注解。

里面的value参照java.text.SimpleDateFormatNumberFormat 数字转换,用String去接收excel数字格式的数据会调用这个注解里面的value参照java.text.DecimalFormat。

EasyExcel相关参数readListener 监听器,在读取数据的过程中会不断的调用监听器converter 转换器,默认加载了很多转换器也可以自定义,如果使用的是registerConverter,那么该转换器是全局的,如果要对单个字段生效,可以在ExcelProperty注解的converter指定转换器。

headRowNumber 需要读的表格有几行头数据默认有一行头,也就是认为第二行开始起为数据head 与clazz二选一读取文件头对应的列表,会根据列表匹配数据,建议使用classautoTrim 字符串、表头等数据自动trim。

sheetNo 需要读取Sheet的编码,建议使用这个来指定读取哪个SheetsheetName 根据名字去匹配Sheet,excel 2003不支持根据名字去匹配导出建立导出对应实体类@Data@Builder

public class RespCustomerDailyImport {    @ExcelProperty("客户编码")    private String customerName;    @ExcelProperty(

"MIS编码")    private String misCode;    @ExcelProperty("月度滚动额")    private BigDecimal monthlyQuota;    @ExcelProperty(

"最新应收账款余额")    private BigDecimal accountReceivableQuota;    @NumberFormat("#.##%")    @ExcelProperty(

"本月利率(年化)")    private BigDecimal dailyInterestRate;}Controller代码@GetMapping("/export")public void export

(HttpServletResponse response) throws IOException {    // 生成数据    List respCustomerDailyImports = Lists.newArrayList();

for (int i = 0; i < 50; i++) {        RespCustomerDailyImport respCustomerDailyImport = RespCustomerDailyImport.builder()

                .misCode(String.valueOf(i))                .customerName("customerName" + i)                .monthlyQuota(new BigDecimal(String.valueOf(i)))

                .accountReceivableQuota(new BigDecimal(String.valueOf(i)))                .dailyInterestRate(new BigDecimal(String.valueOf(i))).build();

        respCustomerDailyImports.add(respCustomerDailyImport);    }    response.setContentType("application/vnd.ms-excel"

);    response.setCharacterEncoding("utf-8");    // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系    String fileName = URLEncoder.encode(

"导出", "UTF-8");    response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx"

);    EasyExcel.write(response.getOutputStream(), RespCustomerDailyImport.class)            .sheet("sheet0"

)            // 设置字段宽度为自动调整,不太精确            .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())

            .doWrite(respCustomerDailyImports);}导出效果

导出相关常用API注解ExcelProperty 指定写到第几列,默认根据成员变量排序value指定写入的名称,默认成员变量的名字ExcelIgnore 默认所有字段都会写入excel,这个注解会忽略这个字段。

DateTimeFormat 日期转换,将Date写到excel会调用这个注解里面的value参照java.text.SimpleDateFormatNumberFormat 数字转换,用Number写excel会调用这个注解。

里面的value参照java.text.DecimalFormatEasyExcel相关参数needHead 监听器是否导出头useDefaultStyle 写的时候是否是使用默认头head 与clazz二选一。

写入文件的头列表,建议使用classautoTrim 字符串、表头等数据自动trimsheetNo 需要写入的编码默认0sheetName 需要些的Sheet名称,默认同sheetNo总结可以看出不管是excel的读取还是写入,都是一个注解加上一行代码完成,可以让我们少些很多解析的代码,极大减少了重复的工作量。

当然这两个例子使用了最简单的方式,EasyExcel还支持更多场景,例如读,可以读多个sheet,也可以解析一行数据或者多行数据做一次入库操作;写的话,支持复杂头,指定列写入,重复多次写入,多个sheet写入,根据模板写入等等。

更多的例子可以去参考EasyExcel官方文档  作者 |  风雨兼程_ad4d来源 | jianshu.com/p/8f3defdc76d4

76套java从入门到精通实战课程分享流弊!2021最新 Spring 面试题SpringBoot实现的网页版聊天室项目如何优化SQL语句,看这篇就够了 !一篇搞定Redis6(完整版)加锋哥微信: java1239

围观锋哥朋友圈,每天推送Java干货!

亲爱的读者们,感谢您花时间阅读本文。如果您对本文有任何疑问或建议,请随时联系我。我非常乐意与您交流。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

大众 新闻51567