easy excel导出标题实现国际化

666 阅读2分钟

最近公司项目需要实现国际化,参考easy excel官方文档对导出的文件对国际化支持不是很友好。

先来看官方文档实现的方案



    /**
     * 可变标题处理(包括标题国际化等)
     * <p>
     * 简单的说用List<List<String>>的标题 但是还支持注解
     * <p>
     * 1. 创建excel对应的实体对象 参照{@link ConverterData}
     * <p>
     * 2. 直接写即可
     */
    @Test
    public void variableTitleWrite() {
        // 写法1
        String fileName = TestFileUtil.getPath() + "variableTitleWrite" + System.currentTimeMillis() + ".xlsx";
        // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
        EasyExcel.write(fileName, ConverterData.class).head(variableTitleHead()).sheet("模板").doWrite(data());
    }

    private List<List<String>> variableTitleHead() {
        List<List<String>> list = ListUtils.newArrayList();
        List<String> head0 = ListUtils.newArrayList();
        head0.add("string" + System.currentTimeMillis());
        List<String> head1 = ListUtils.newArrayList();
        head1.add("number" + System.currentTimeMillis());
        List<String> head2 = ListUtils.newArrayList();
        head2.add("date" + System.currentTimeMillis());
        list.add(head0);
        list.add(head1);
        list.add(head2);
        return list;
    }    private List<List<String>> variableTitleHead() {
        List<List<String>> list = new ArrayList<>();
        List<String> head0 = new ArrayList<>();
        head0.add("string" + System.currentTimeMillis());
        List<String> head1 = new ArrayList<>();
        head1.add("number" + System.currentTimeMillis());
        List<String> head2 = new ArrayList<>();
        head2.add("date" + System.currentTimeMillis());
        list.add(head0);
        list.add(head1);
        list.add(head2);
        return list;
    }

@Getter
@Setter
@EqualsAndHashCode
public class ConverterData {
    /**
     * 我想所有的 字符串起前面加上"自定义:"三个字
     */
    @ExcelProperty(value = "字符串标题", converter = CustomStringStringConverter.class)
    private String string;
    /**
     * 我想写到excel 用年月日的格式
     */
    @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
    @ExcelProperty("日期标题")
    private Date date;
    /**
     * 我想写到excel 用百分比表示
     */
    @NumberFormat("#.##%")
    @ExcelProperty(value = "数字标题")
    private Double doubleData;
}

关键代码是head(variableTitleHead()),相当于由variableTitleHead直接返回经过国际化处理后的标题。 这种实现方式个人觉得不是很优雅,原因有两点

  • 原pojo类中ExcelProperty注解失去意义
  • 第二点,也是最重要的一点,就是当需要根据前端参数导出指定字段且按照指顺序排序的时,variableTitleHead方法中需要返回需要自己实现以上逻辑,以保证生成的表头和数据字段一致且顺序一致。显然,这段逻辑与国际化无关,不应该写在这里,对于有代码强迫症的怎么能忍呢😂。

查看easy excel源码后,找到了一个算是投机取巧的办法,主要是改动代码相对减少了不少。关键代码如下

//需要导出的字段,从前端获取
Collection<String> includeFields=null;
//导出的数据
Collection<DataPOjO> list= null;

try (ExcelWriter excelWriter = EasyExcel.write(outputMessage.getBody(), excelData.getType()).includeColumnFieldNames(includeFields).build()) {
    WriteSheet writeSheet = EasyExcel.writerSheet("sheet名称")
            .orderByIncludeColumn(true)
            .build();
  //ClassUtils为easy excel核心包中的类
  //一定要在write方法之前执行下面2行代码
    FieldCache fieldCache = ClassUtils.declaredFields(DataPOjO.class, excelWriter.writeContext().currentWriteHolder());
    //处理国际化
    processHead(fieldCache);
    excelWriter.write(list, writeSheet);
}catch (Exception e){
    log.error("",e);
}

//数据
@Data
class DataPOjO{
    @ExcelProperty(value = "${i18n.key}")
    private String name;
}

Pattern pattern= Pattern.compile("\$\{(.*)\}");

private void processHead(FieldCache fieldCache) {
    Map<Integer, FieldWrapper> headMap = fieldCache.getSortedFieldMap();
    for (Map.Entry<Integer, FieldWrapper> integerHeadEntry : headMap.entrySet()) {
        FieldWrapper value = integerHeadEntry.getValue();
        String[] heads = value.getHeads();
        if(heads!=null&&heads.length >= 1){
            String headStr = heads[0];
            Matcher matcher = pattern.matcher(headStr);
            if (matcher.find()) {
                String i18nKey = matcher.group(1);
                value.setHeads(new String[]{I18nMsgUtil.getMessage(i18nKey)});
            }
        }
    }
}

通过processHead方法把原来通过ExcelProperty获取的字段名替换成i18n资源文件中的配置的名称