HSSFWorkbook中输出文件时的坑:The workbook already contains a sheet named

情景

   在工作中经常会遇到导入导出之类的需求,或者说根据用户的情况动态生成一些文件,一般情况下出错误的应该都在文件的数据中或者格式啥的,但有一次用户下载失败,经过日志排查发现是sheet名称重复了。

   小小的脑袋大大的疑惑,当时我就纳闷了,sheet和文件名称都是动态生成的,而且后台有做过重复校验,怎么还能sheet名称重复呢?

原因

   经过debug调试,发现最终错误确实是sheet名称重复,经过网上的查询和源码分析,发现了如下原因:

    public HSSFSheet createSheet(String sheetname) {
        if (sheetname == null) {
            throw new IllegalArgumentException("sheetName must not be null");
        } else if (this.workbook.doesContainsSheetName(sheetname, this._sheets.size())) {
            throw new IllegalArgumentException("The workbook already contains a sheet named '" + sheetname + "'");
        } else {
            HSSFSheet sheet = new HSSFSheet(this);
            this.workbook.setSheetName(this._sheets.size(), sheetname);
            this._sheets.add(sheet);
            boolean isOnlySheet = this._sheets.size() == 1;
            sheet.setSelected(isOnlySheet);
            sheet.setActive(isOnlySheet);
            return sheet;
        }
    }

   这段是创建sheet表的源码,大家可以看到在第二个if判断的时候,如果失败就会报

The workbook already contains a sheet named
的错误,查看一下这个判断方法。

   这里一共有两个坑,第一个坑是sheet名称长度,可以看到红框中,如果名称的长度大于31,这里就自动会将名称截取到31个字符,那么我们就可以判断出当时的错误,原因就是用户导出的数据中存在了两个长度大于31并且前31个字符都相同的情况(一般系统都会有复制操作,可能就是这个导致的名称重复度太高)。

   第二个坑就是下面这句了。

if (aName.equalsIgnoreCase(bName)) {
   return true;
}

   注意了老铁们,“equalsIgnoreCase”方法是忽略大小写的比较,而我们一般在判断重复的时候,习惯性使用的是equals方法,这个是区分大小写的比较,当出现sheet名称是“测试A”和“测试a”的时候,也会报出

The workbook already contains a sheet named

   如果排查的话也是比较费劲的事情,这就考验日志打印的怎么样了,最离谱的是,一般我们是不会打印sheet名称到日志的,顶多打印出文件名称啥的,所以老铁们一定要注意啦!

解决方案

   关于第一种名称过长的问题,一般的解决方案是手动截取字符串,比如:

        if (!StringUtils.isEmpty(sheetName) && sheetName.length() > 31) {
            sheetName = sheetName.substring(0, 26) + "...";
        }

   这是最方便的解决方法,要么就是抛出异常(一般业务是不会允许的)。

   也有特殊情况,这就要看公司的业务怎么定了,有的可能会有合并数据(这个真的是噩梦)或者分组处理sheet名称的情况,分组处理sheet名称就是出现名称都大于一定个数字符,且部分重复的情况,就截取一段相同的文字,之后依次加上xxx1或者xxx2等这种,当然没毛病,就是处理起来稍微麻烦点,解决思路:

1. 外部定义一个HashMap,key是sheet名称,Value是业务自定义要拼接的字符

2. 每次创建sheet前做几个操作,如果长度超过了限制,就把需要截取的字符搞出来,放到这个Map中,后面每次都先查一下这个Map。

3. 如果匹配到了对应的key,拿出对应的Value,做加1或者其他操作就行。

   第二种忽略大小写的,这是一个硬伤,无论是代码还是Excel本身都无法绕过大小写的判断,出现这种情况就看业务了,如果能直接抛出错误那最好了,不能那就只能特殊处理了(最好和业务刚一下,因为本身这东西就不支持,特殊处理增加不必要且没啥用的工作量)。

好了,希望对大家有用,如有错误欢迎指正。