Просмотр исходного кода

扩展tools-poi,增加Excel读取为字符串数组的方法支持;修复xls格式读取最后一行bug;增加单元测试文件及代码

Signed-off-by: luoshi <luoshi@qmth.com.cn>
luoshi 1 год назад
Родитель
Сommit
63e5f08ac3

+ 32 - 7
tools-poi/src/main/java/com/qmth/boot/tools/excel/ExcelReader.java

@@ -1,6 +1,7 @@
 package com.qmth.boot.tools.excel;
 
 import com.qmth.boot.tools.excel.enums.ExcelType;
+import com.qmth.boot.tools.excel.handler.impl.DataArrayHandler;
 import com.qmth.boot.tools.excel.handler.impl.DataMapHandler;
 import com.qmth.boot.tools.excel.handler.impl.ObjectHandler;
 import com.qmth.boot.tools.excel.listener.ObjectCollector;
@@ -22,9 +23,11 @@ public class ExcelReader {
     private int columnNameRow;
 
     /**
-     * @param type          - XLSX / XLS
-     * @param ins           - Excel内容输入流
-     * @param columnNameRow - Excel中表头的行号,从0开始
+     * 初始化带表头的Excel解析工具
+     *
+     * @param type          文件格式,XLSX或XLS
+     * @param ins           文件输入流
+     * @param columnNameRow 表头所在行号,0表示第一行
      * @return
      */
     public static ExcelReader create(ExcelType type, InputStream ins, int columnNameRow) {
@@ -52,7 +55,7 @@ public class ExcelReader {
     /**
      * 解析Excel内容为指定类型的对象,只返回转换成功的对象
      *
-     * @param objectType - 对象类型,使用@ExcelColumn注解修饰字段
+     * @param objectType 对象类型,使用@ExcelColumn注解修饰字段
      * @return
      * @throws Exception
      */
@@ -74,11 +77,23 @@ public class ExcelReader {
         return collector.getObjectList();
     }
 
+    /**
+     * 解析Excel内容为字符串数组,所有数组长度与表头保持一致
+     *
+     * @return
+     * @throws Exception
+     */
+    public List<String[]> getDataArrayList() throws Exception {
+        ObjectCollector<String[]> collector = new ObjectCollector<>();
+        readDataArray(collector);
+        return collector.getObjectList();
+    }
+
     /**
      * 遍历Excel内容,转换为指定类型的对象,并输出给指定的监听器
      *
-     * @param objectType - 对象类型,使用@ExcelColumn注解修饰字段
-     * @param listener   - 指定的对象监听器
+     * @param objectType 对象类型,使用@ExcelColumn注解修饰字段
+     * @param listener   指定的对象监听器
      * @throws Exception
      */
     public <E> void readObject(Class<E> objectType, ObjectListener<E> listener) throws Exception {
@@ -88,13 +103,23 @@ public class ExcelReader {
     /**
      * 遍历Excel内容,转换为默认的DataMap对象,并输出给指定的监听器
      *
-     * @param listener - 指定的对象监听器
+     * @param listener 指定的对象监听器
      * @throws Exception
      */
     public void readDataMap(ObjectListener<DataMap> listener) throws Exception {
         parser.parse(ins, new DataMapHandler(columnNameRow, listener));
     }
 
+    /**
+     * 遍历Excel内容,转换为字符串数组,并输出给指定的监听器
+     *
+     * @param listener 指定的对象监听器
+     * @throws Exception
+     */
+    public void readDataArray(ObjectListener<String[]> listener) throws Exception {
+        parser.parse(ins, new DataArrayHandler(columnNameRow, listener));
+    }
+
     /**
      * 获取列名数组,不包含列名的情况下可能返回空
      *

+ 17 - 17
tools-poi/src/main/java/com/qmth/boot/tools/excel/ExcelWriter.java

@@ -22,9 +22,9 @@ import java.util.concurrent.atomic.AtomicInteger;
 public abstract class ExcelWriter {
 
     /**
-     * 创建Excel生成工具
+     * 初始化带表头的Excel生成工具
      *
-     * @param type - XLSX / XLS
+     * @param type 文件格式,XLSX或XLS
      * @return
      */
     public static ExcelWriter create(ExcelType type) {
@@ -40,10 +40,10 @@ public abstract class ExcelWriter {
     /**
      * 创建sheet并写入String[]内容
      *
-     * @param sheetName    - sheet名称
-     * @param titles       - 前置的标题,可以为空
-     * @param columnNames  - 数据列名称
-     * @param dataIterator - String[]迭代器
+     * @param sheetName    sheet名称
+     * @param titles       前置的标题,可以为空
+     * @param columnNames  数据列名称
+     * @param dataIterator String[]迭代器
      */
     public void writeDataArrays(String sheetName, String[] titles, String[] columnNames,
             Iterator<String[]> dataIterator) {
@@ -62,10 +62,10 @@ public abstract class ExcelWriter {
     /**
      * 创建sheet并写入DataMap对象
      *
-     * @param sheetName    - sheet名称
-     * @param titles       - 前置的标题,可以为空
-     * @param columnNames  - 数据列名称
-     * @param dataIterator - DataMap对象迭代器
+     * @param sheetName    sheet名称
+     * @param titles       前置的标题,可以为空
+     * @param columnNames  数据列名称
+     * @param dataIterator DataMap对象迭代器
      */
     public void writeDataMaps(String sheetName, String[] titles, String[] columnNames, Iterator<DataMap> dataIterator) {
         Sheet sheet = getWorkbook().createSheet(sheetName);
@@ -83,10 +83,10 @@ public abstract class ExcelWriter {
     /**
      * 创建sheet并写入自定义对象
      *
-     * @param sheetName      - sheet名称
-     * @param titles         - 前置标题,可以为空
-     * @param objectType     - 自定义对象类型
-     * @param objectIterator - 自定义对象迭代器
+     * @param sheetName      sheet名称
+     * @param titles         前置标题,可以为空
+     * @param objectType     自定义对象类型
+     * @param objectIterator 自定义对象迭代器
      * @param <E>
      */
     public <E> void writeObjects(String sheetName, String[] titles, Class<E> objectType, Iterator<E> objectIterator) {
@@ -104,7 +104,7 @@ public abstract class ExcelWriter {
     }
 
     private void writeTitle(Sheet sheet, AtomicInteger rowIndex, String[] titles) {
-        if (titles != null) {
+        if (titles != null && titles.length > 0) {
             for (String title : titles) {
                 Row row = sheet.createRow(rowIndex.getAndIncrement());
                 Cell cell = row.createCell(0);
@@ -131,12 +131,12 @@ public abstract class ExcelWriter {
         }
     }
 
-    private void writeData(Sheet sheet, AtomicInteger rowIndex, String[] columnNames, DataMap data) {
+    private void writeData(Sheet sheet, AtomicInteger rowIndex, String[] columnNames, DataMap dataMap) {
         Row dataRow = sheet.createRow(rowIndex.getAndIncrement());
         int columnIndex = 0;
         for (String name : columnNames) {
             Cell cell = dataRow.createCell(columnIndex++);
-            cell.setCellValue(data.getOrDefault(name, StringUtils.EMPTY));
+            cell.setCellValue(dataMap.getOrDefault(name, StringUtils.EMPTY));
         }
     }
 

+ 47 - 0
tools-poi/src/main/java/com/qmth/boot/tools/excel/handler/impl/DataArrayHandler.java

@@ -0,0 +1,47 @@
+package com.qmth.boot.tools.excel.handler.impl;
+
+import com.qmth.boot.tools.excel.handler.RowDataHandler;
+import com.qmth.boot.tools.excel.listener.ObjectListener;
+import com.qmth.boot.tools.excel.model.RowData;
+
+public class DataArrayHandler implements RowDataHandler {
+
+    private ObjectListener<String[]> listener;
+
+    private int columnNameRow;
+
+    private String[] columnNames;
+
+    public DataArrayHandler(int columnNameRow, ObjectListener<String[]> listener) {
+        this.listener = listener;
+        this.columnNameRow = columnNameRow;
+    }
+
+    @Override
+    public void handle(RowData rowData) {
+        if (rowData.getRow() == columnNameRow && columnNames == null) {
+            buildColumnNames(rowData);
+        } else if (rowData.getRow() > columnNameRow && columnNames != null) {
+            convertDataArray(rowData);
+        }
+    }
+
+    @Override
+    public String[] getColumnNames() {
+        return columnNames;
+    }
+
+    private void buildColumnNames(RowData rowData) {
+        columnNames = rowData.getValues();
+    }
+
+    private void convertDataArray(RowData rowData) {
+        String[] array = new String[columnNames.length];
+        String[] values = rowData.getValues();
+        for (int i = 0; i < array.length && i < values.length; i++) {
+            array[i] = values[i];
+        }
+        listener.output(array);
+    }
+
+}

+ 8 - 11
tools-poi/src/main/java/com/qmth/boot/tools/excel/parser/impl/XlsParser.java

@@ -4,6 +4,7 @@ import com.qmth.boot.tools.excel.handler.RowDataHandler;
 import com.qmth.boot.tools.excel.model.RowData;
 import com.qmth.boot.tools.excel.parser.ExcelParser;
 import org.apache.poi.hssf.eventusermodel.*;
+import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord;
 import org.apache.poi.hssf.record.*;
 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 
@@ -91,22 +92,18 @@ public class XlsParser implements HSSFListener, ExcelParser {
         //}
 
         // Handle end of row
-        //if (record instanceof LastCellOfRowDummyRecord) {
-        // End the row
-        //if (rowData != null) {
-        //    consumer.accept(rowData);
-        //}
-        //}
+        if (record instanceof LastCellOfRowDummyRecord) {
+            //End the row
+            if (rowData != null) {
+                handler.handle(rowData);
+                rowData = null;
+            }
+        }
     }
 
     private RowData getRowData(int row) {
         if (rowData == null) {
             rowData = new RowData(row);
-        } else {
-            if (rowData.getRow() != row) {
-                handler.handle(rowData);
-                rowData = new RowData(row);
-            }
         }
         return rowData;
     }

+ 27 - 13
tools-poi/src/test/java/com/qmth/boot/test/tools/excel/read/ExcelReadTest.java

@@ -1,31 +1,45 @@
 package com.qmth.boot.test.tools.excel.read;
 
 import com.qmth.boot.test.tools.excel.model.ExcelTestEntity;
+import com.qmth.boot.test.tools.excel.model.GenderEnum;
 import com.qmth.boot.tools.excel.ExcelReader;
 import com.qmth.boot.tools.excel.enums.ExcelType;
 import com.qmth.boot.tools.excel.listener.ObjectCollector;
-import com.qmth.boot.tools.excel.model.DataMap;
+import com.qmth.boot.tools.models.ByteArray;
+import org.apache.commons.lang3.time.DateUtils;
+import org.junit.Assert;
+import org.junit.Test;
 
-import java.io.FileInputStream;
 import java.util.List;
 
 public class ExcelReadTest {
 
-    //@Test
+    @Test
     public void testObjectRead() throws Exception {
-        FileInputStream ins = new FileInputStream("/Users/luoshi/Downloads/test.xlsx");
         ObjectCollector<ExcelTestEntity> listener = new ObjectCollector<>();
-        ExcelReader.create(ExcelType.XLSX, ins, 0).readObject(ExcelTestEntity.class, listener);
-        System.out.println(listener.getObjectList().size());
-        ins.close();
+        ExcelReader.create(ExcelType.XLSX, ByteArray.fromResource("test.xlsx").toInputStream(), 0)
+                .readObject(ExcelTestEntity.class, listener);
+        List<ExcelTestEntity> list = listener.getObjectList();
+        Assert.assertEquals(2, list.size());
+        Assert.assertEquals(list.get(1).getId(), Long.valueOf(3));
+        Assert.assertEquals(list.get(1).getName(), "李四");
+        Assert.assertEquals(list.get(1).getScore(), Double.valueOf(100.6));
+        Assert.assertFalse(list.get(1).isEnable());
+        Assert.assertEquals(list.get(1).getGender(), GenderEnum.FEMALE);
+        Assert.assertEquals(list.get(1).getTime(), DateUtils.parseDate("2023-11-30", "yyyy-MM-dd"));
     }
 
-    //@Test
-    public void testDataMapRead() throws Exception {
-        FileInputStream ins = new FileInputStream("/Users/luoshi/Downloads/test.xls");
-        List<DataMap> list = ExcelReader.create(ExcelType.XLS, ins, 2).getDataMapList();
-        System.out.println(list.size());
-        ins.close();
+    @Test
+    public void testDataArrayRead() throws Exception {
+        List<String[]> list = ExcelReader.create(ExcelType.XLS, ByteArray.fromResource("test.xls").toInputStream(), 1)
+                .getDataArrayList();
+        Assert.assertEquals(4, list.size());
+        Assert.assertEquals(list.get(0)[0], "1");
+        Assert.assertEquals(list.get(0)[2], "0.56");
+        Assert.assertEquals(list.get(2)[1], "李四");
+        Assert.assertEquals(list.get(2)[3], "否");
+        Assert.assertNull(list.get(3)[4]);
+        Assert.assertEquals(list.get(3)[5], "2023/01/01");
     }
 
 }

+ 14 - 8
tools-poi/src/test/java/com/qmth/boot/test/tools/excel/write/ExcelWriteTest.java

@@ -4,15 +4,17 @@ import com.qmth.boot.test.tools.excel.model.ExcelTestEntity;
 import com.qmth.boot.test.tools.excel.model.GenderEnum;
 import com.qmth.boot.tools.excel.ExcelWriter;
 import com.qmth.boot.tools.excel.enums.ExcelType;
+import org.junit.Assert;
+import org.junit.Test;
 
-import java.io.FileOutputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.util.Collections;
 import java.util.Date;
 
 public class ExcelWriteTest {
 
-    //@Test
+    @Test
     public void testXlsx() throws IOException {
         ExcelTestEntity entity = new ExcelTestEntity();
         entity.setId(System.currentTimeMillis());
@@ -21,13 +23,15 @@ public class ExcelWriteTest {
         entity.setGender(GenderEnum.FEMALE);
         entity.setTime(new Date());
         entity.setEnable(true);
+        ByteArrayOutputStream ous = new ByteArrayOutputStream();
         ExcelWriter writer = ExcelWriter.create(ExcelType.XLSX);
         writer.writeObjects("sheet1", new String[] { "标题" }, ExcelTestEntity.class,
                 Collections.singletonList(entity).iterator());
-        writer.output(new FileOutputStream("/Users/luoshi/Downloads/write.xlsx"));
+        writer.output(ous);
+        Assert.assertTrue(ous.toByteArray().length > 0);
     }
 
-    //@Test
+    @Test
     public void testXls() throws IOException {
         ExcelTestEntity entity = new ExcelTestEntity();
         entity.setId(System.currentTimeMillis() * 100);
@@ -35,12 +39,14 @@ public class ExcelWriteTest {
         entity.setScore(65d);
         entity.setGender(GenderEnum.MALE);
         entity.setTime(new Date());
-        entity.setEnable(true);
+        entity.setEnable(false);
         entity.setClassName("   ");
         entity.setTeacher("!@#");
+        ByteArrayOutputStream ous = new ByteArrayOutputStream();
         ExcelWriter writer = ExcelWriter.create(ExcelType.XLS);
-        writer.writeObjects("sheet1", new String[] { "标题" }, ExcelTestEntity.class,
-                Collections.singletonList(entity).iterator());
-        writer.output(new FileOutputStream("/Users/luoshi/Downloads/write.xls"));
+        writer.writeDataArrays("sheet1", new String[] { "考生表" }, new String[] { "编号", "姓名" },
+                Collections.singletonList(new String[] { "1", "张三" }).iterator());
+        writer.output(ous);
+        Assert.assertTrue(ous.toByteArray().length > 0);
     }
 }

BIN
tools-poi/src/test/resources/test.xls


BIN
tools-poi/src/test/resources/test.xlsx