Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能。

注意事项

1、运用多态,excel主要有.xls结尾(2003版本)和. xlsx(2007版本)两种类型结尾的文件,分别需要用HSSFWorkbook对象对.xls文件进行读取,用XSSFWorkbook对象对.xlsx文件进行读取,直接使用他们共同的父类Workbook进行初始化对象有利于代码的易用性。

2、通过流的方式初始化工作簿对象(Workbook),可以通过new XSSFWorkbook(文件绝对路径)和new XSSFWorkbook(输入流)两种方式初始化对象,但是假如我们只是通过修改.xls文件的后缀名为.xlsx,这样子当我们用new XSSFWorkbook(文件绝对路径)来读取文件的时候就会报错,因为他本身就不是一个2007版本的excel类型的文件,读取会报错;假如我们是通过流的方式的话,可以避免这种情况,我们即使你修改了文件的后缀名,我们依然在初始化的时候能获取到该对象是.xls类型文件,使用HSSFWorkbook对象进行处理,即能得出正确的结果。


添加了判断表头是否符合规范,允许表头对象的位置不同。进行了一定的解耦合。

注意: 由于以前的Cell.CELL_TYPE_STRING,获取Cell的类型已经过时,并且poi导入excel判断单元格类型及转换又有比较多的问题,从excel中识别出来数据类型会有偏差,故改成 返回该单元格相应的类型的值 如下的 getRightTypeCell 方法:

备注: 此处的checkDate是明确的日期类型

/**
 * 描述:
 * 从excel中导入单据数据业务实现
 *
 * @author selfimpr626
 * @version 1.0.0.20190123
 * @since JDK V1.8
 */
@Component
public class ImportExcelUtil4Info {

    private static InfoService infoService;

    private ImportExcelUtil4Info() {}

    @Autowired
    public void setInfoService(InfoService infoService) {
        ImportExcelUtil4Info.infoService = infoService;
    }

    /**
     * @param cell 一个单元格的对象
     * @return 返回该单元格相应的类型的值
     */
    public static Object getRightTypeCell(Cell cell, String what){
        Object object = null;
        if (cell!=null && !"".equals(cell)) {
            switch (cell.getCellTypeEnum()) {
                case STRING:
                    object = cell.getRichStringCellValue().getString();
                    break;
                case NUMERIC:
                    if ("checkDate".equals(what)) {
                        Instant instant = cell.getDateCellValue().toInstant();
                        ZoneId zone = ZoneId.systemDefault();
                        object = LocalDateTime.ofInstant(instant, zone);
                    }else {
                        object = cell.getNumericCellValue();
                        if(object instanceof Double) {
                            object = String.valueOf(object);
                        }
                    }
                    break;
                case BOOLEAN:
                    object = cell.getBooleanCellValue();
                    break;
                case BLANK:
                    object = "";
                    break;
                case FORMULA:
                    // 待定
                    break;
                default:
                    object = cell.toString();
                    break;
            }
        }
        return object;
    }

    /**
     * 读取出filePath中的所有数据信息
     * @param filePath excel文件的绝对路径
     *
     */
    public static List<Info> getDataFromExcel(String filePath, Long relId)
    {
        List<Map<String,Integer>> list = new ArrayList<Map<String, Integer>>();
        //判断是否为excel类型文件
        if(!filePath.endsWith(".xls")&&!filePath.endsWith(".xlsx")) {
        }
        FileInputStream fis =null;
        Workbook wookbook = null;
        int flag = 1;
        try {
            //获取一个绝对地址的流
            fis = new FileInputStream(filePath);
        } catch(Exception e) {
            e.printStackTrace();
        }

        try {
            //2003版本的excel,用.xls结尾
            wookbook = new HSSFWorkbook(fis);//得到工作簿
        } catch (Exception ex)
        {
            //ex.printStackTrace();
            try {
                //这里需要重新获取流对象,因为前面的异常导致了流的关闭———加了这一行
                fis = new FileInputStream(filePath);
                //2007版本的excel,用.xlsx结尾
                wookbook = new XSSFWorkbook(filePath);//得到工作簿
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        //得到一个工作表
        Sheet sheet = wookbook.getSheetAt(0);
        //获得表头
        Row rowHead = sheet.getRow(4);
        //根据不同的data放置不同的表头
        Map<Object,Integer> headMap = new HashMap<Object, Integer>();

        if(rowHead == null) {
            throw new RuntimeException("表头与要导入的数据库不对应");
        }
        //判断表头是否合格  ------ 有多少列
        if(rowHead.getPhysicalNumberOfCells() != 2) {
            throw new RuntimeException("表头列数与要导入的数据库不对应");
        }
        try {
            // 根据表格有多少列
            while (flag <= 2)
            {
                Cell cell = rowHead.getCell(flag);
                if (cell != null && !"".equals(cell)) {
                    if (getRightTypeCell(cell, null).toString() != null && !"".equals(getRightTypeCell(cell, null).toString()) ) {
                        if (getRightTypeCell(cell, null).toString().equals("号码")) {
                            headMap.put("code", flag);
                        } else if (getRightTypeCell(cell, null).toString().equals("检查时间")) {
                            // 对应实体:检查时间
                            headMap.put("checkDate", flag);
                        } 
                    }
                }
                flag++;
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("表头不合规范,请修改后重新导入");
        }

        //获得数据的总行数
        int totalRowNum = sheet.getLastRowNum();
        // 最终返回的List<info>
        List<Info> infoList = new ArrayList<>();

        if(0 == totalRowNum) {
            throw new RuntimeException("Excel内没有数据");
        }
        Cell codeCell = null,checkDateCell = null;
        //获得所有数据
        for(int i = 1 ; i <= totalRowNum ; i++) {
            //获得第i行对象
            Row row = sheet.getRow(i+4);
            if(row != null && !"".equals(row) && row.getCell(i) != null) {
                try {
                    codeCell = row.getCell(headMap.get("code"));
                    checkDateCell = row.getCell(headMap.get("checkDate"));
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new RuntimeException("获取单元格错误,请检查表头或数据是否正确");
                }

                try {
                    Info info = new Info();
                    // 号码
                    String code = (String) getRightTypeCell(codeCell, null);
                    info.setCode(code);
                    // 检查时间
                    info.setCheckDate((LocalDateTime) getRightTypeCell(checkDateCell, "checkDate"));
                    
                    // 对每一行的号码数据判断:Excel中存在同一号码(唯一)
                    if(info.getPatientCode() != null) {
                        Info pf = infoService.findByCode(info.getCode(), relId);
                        if(pf != null) {
                            throw new RuntimeException("Excel中存在系统已有单据");
                        }
                    }else {
                        throw new RuntimeException("Excel中存在号码字段缺失");
                    }
                    infoList.add(info);
                } catch (ClassCastException e) {
                    e.printStackTrace();
//                    System.out.println("数据不全是数字或全部是文字!");
                }
            }
        }
        if(infoList.size() >0) {
            // 返回构造好的实体数据List<info>
            return infoList;
        }else {
            return null;
        }
    }
}