[alibaba/easyexcel]:bug: fix 当进行加密导出时,由于输出流未关闭导致的文件损坏异常

2024-05-11 480 views
9
当给 excel 进行加密导出时,生成的文件无法正常打开,提示文件损坏。

image

此问题在 macOs 可直接复现,windows 下 jdk11 可以复现

测试代码:
public class ExcelTest {

    public static void main(String[] args) {
        // 写法2
        String fileName =  System.currentTimeMillis() + ".xlsx";
        // 这里 需要指定写用哪个class去写
        ExcelWriter excelWriter = null;
        try {
            excelWriter = EasyExcel.write(fileName, DemoData.class).password("123456").build();
            WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
            excelWriter.write(data(), writeSheet);
        } finally {
            // 千万别忘记finish 会帮忙关闭流
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }
    }

    private static List<DemoData> data() {
        List<DemoData> list = new ArrayList<DemoData>();
        for (int i = 0; i < 10; i++) {
            DemoData data = new DemoData();
            data.setString("字符串" + i);
            data.setDate(new Date());
            data.setDoubleData(0.56);
            list.add(data);
        }
        return list;
    }

    @Data
    public static class DemoData {
        @ExcelProperty("字符串标题")
        private String string;
        @ExcelProperty("日期标题")
        private Date date;
        @ExcelProperty("数字标题")
        private Double doubleData;
        /**
         * 忽略这个字段
         */
        @ExcelIgnore
        private String ignore;
    }
}
问题定位:

在 com.alibaba.excel.context.WriteContextImpl#openFileSystemAndEncrypt 方法中,OutputStream 使用后未正确关闭

private POIFSFileSystem openFileSystemAndEncrypt(File file) throws Exception {
        POIFSFileSystem fileSystem = new POIFSFileSystem();
        Encryptor encryptor = (new EncryptionInfo(EncryptionMode.standard)).getEncryptor();
        encryptor.confirmPassword(this.writeWorkbookHolder.getPassword());
        OPCPackage opcPackage = null;
        try {
            opcPackage = OPCPackage.open(file, PackageAccess.READ_WRITE);
            // 该输出流应关闭
            OutputStream outputStream = encryptor.getDataStream(fileSystem);
            opcPackage.save(outputStream);
        } finally {
            if (opcPackage != null) {
                opcPackage.close();
            }
        }
        return fileSystem;
    }

回答

8

我也遇到这个问题了,这个问题解决了吗

8

我也遇到这个问题了,这个问题解决了吗

@GaryGuo111111 由于此 PR 未合并,所以我暂时通过本地 class 覆盖的方式修复了,具体方式可以参看

我的开源项目 ballcat 中的 ballcat-spring-boot-starter-easyexcel

4

使用springboot方式怎么覆盖呢?

7

使用springboot方式怎么覆盖呢?

@GaryGuo111111 一样的.

  1. 在 com.alibaba.excel.context 包下放入我提交的 WriteContextImpl.java 类,保证其全类名和依赖中的一致即可达到覆盖效果。

  2. 另外需要排除其 低版本 poi,升级使用 4.1.2 版本

      <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.poi</groupId>
                    <artifactId>poi</artifactId>
                </exclusion>
                <exclusion>
                  <groupId>org.apache.poi</groupId>
                  <artifactId>poi-ooxml</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.poi</groupId>
                    <artifactId>poi-ooxml-schemas</artifactId>
                </exclusion>
            </exclusions>
    </dependency>
      <dependency>
          <groupId>org.apache.poi</groupId>
          <artifactId>poi-ooxml</artifactId>
          <version>4.1.2</version>
      </dependency>
6

使用springboot方式怎么覆盖呢?

@GaryGuo111111 一样的.

  1. 在 com.alibaba.excel.context 包下放入我提交的 WriteContextImpl.java 类,保证其全类名和依赖中的一致即可达到覆盖效果。
  2. 另外需要排除其 低版本 poi,升级使用 4.1.2 版本
     <dependency>
           <groupId>com.alibaba</groupId>
           <artifactId>easyexcel</artifactId>
           <exclusions>
               <exclusion>
                   <groupId>org.apache.poi</groupId>
                   <artifactId>poi</artifactId>
               </exclusion>
               <exclusion>
                 <groupId>org.apache.poi</groupId>
                 <artifactId>poi-ooxml</artifactId>
               </exclusion>
               <exclusion>
                   <groupId>org.apache.poi</groupId>
                   <artifactId>poi-ooxml-schemas</artifactId>
               </exclusion>
           </exclusions>
    </dependency>
     <dependency>
         <groupId>org.apache.poi</groupId>
         <artifactId>poi-ooxml</artifactId>
         <version>4.1.2</version>
     </dependency>

不想通过包名覆盖的形式覆盖,想用springboot bean加载覆盖,要怎么操作呢?

6

不想通过包名覆盖的形式覆盖,想用springboot bean加载覆盖,要怎么操作呢?

不支持这样方式实现,easyexcel 自身的这些类并没有注册到 spring 容器中

9

是否可以把该pr合入2.x版本中呢

6

你好,我遇到了这个问题然后用这个方式去解决了,很感谢。但是当我使用这种方式复现时问题并没有出来,有点疑惑

0

MacOS,JDK1.8,easyexcel2.2.8,我查看了源码流也是没有关闭的,大佬如果有空是否可以解解惑,非常感谢(帮别人解决的,然后自己想复现一下看看,按issue方式来复现的)