• java 使用 poi 解析excel


    背景:

    web应用经常需要上传文件,有时候需要解析出excel中的数据,如果excel的格式没有问题,那就可以直接解析数据入库。

    工具选择:

    目前jxl和poi可以解析excel,jxl很早就停止维护了,只支持excel-2003也就是xls格式的文件;
     
    poi可支持xls和xlsx格式的文件,经过考察,poi的功能强大很多,所以选择这个工具解析excel。文件上传在之前的一个专题有所提及。
     
    需要如下jar包,jar包见附件,也可在官网下载。
    注意:
    1. 不支持单元格合并的情况,默认表格格式规范,格式规范指:
    从第一行第一列开始,第一行为表头,后面全都是数据,每一个列代表一个字段。没有中途断掉的行或者列,允许单元格为空。
     
    2. 代码初步运用了重构中的方法抽取(extract method)和方法对象抽取(extract method object)技术,代码行数前后没有太大的变化,但是结构明显要清楚一些。
    只不过初步使用,有些抽取不是特别直观,写文件的两个方法没有使用重构技术,此外在单元格类型匹配,也还有深挖的空间,希望读者根据自己的需要,自行定制。
    关于重构技术和解析excel的api,请参考《重构改善既有代码的设计》以及poi的源代码
     
      1 package com.test;
      2 import org.apache.poi.hssf.usermodel.HSSFDataFormat;
      3 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
      4 import org.apache.poi.ss.usermodel.*;
      5 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
      6 import java.io.*;
      7 import java.text.DecimalFormat;
      8 import java.text.SimpleDateFormat;
      9 import java.util.*;
     10 /**
     11  * Created by tm on 2016/4/11.
     12  *
     13  * @author tm
     14  *         使用POI解析和生成excel,暂不支持单元格合并情况,默认是标准格式的excel
     15  *         实测同等的数据量(3张sheet超过2w条),xlsx的速度要远慢于xls的解析速度。
     16  *         同等数据量:
     17  *         xls的解析,如果只取表头,不超过500毫秒;如果取整个数据,时间不超过2000毫秒;
     18  *         而xlsx的解析,如果只取表头,需要2000毫秒左右,如果取整个数据,则在3000毫秒以上。
     19  *         以上耗时不同系统不同硬件有不同结果,仅作为相对比较结果。
     20  * 这个类提供三个功能:
     21  *         1.获得表格的表头所有列,这个功能可以酌情去掉,因为获得表格的所有数据也就获得了所有列.
     22  *         2.获得表格的所有数据
     23  *         3.传入数据写出来
     24  */
     25 public class ExcelUtil {
     26     public static final String SUFFIX_XLS = "xls";
     27     public static final String SUFFIX_XLSX = "xlsx";
     28     public static final String NO_FILE_NULL = "NO FILE NULL";
     29     public static final String NO_FILE_EMPTY = "NO FILE EMPTY";
     30     /**
     31      * 获得表格的列
     32      * @param fileName
     33      * @return
     34      * @throws RuntimeException
     35      */
     36     public static List<Map<String, Object>> getTableSheetCol(String fileName) throws RuntimeException{
     37         if(!valid(fileName))
     38             return null;
     39         else{
     40             if(validExcelFormat(fileName,SUFFIX_XLS)){
     41                 //是excel2003?
     42                 return getXlsSheetItem(fileName);
     43             }
     44             if(validExcelFormat(fileName, SUFFIX_XLSX)){
     45                 //是excel2007以后
     46                 return getXlsxSheetItem(fileName);
     47             }else{
     48                 print("nonsupport file format");
     49                 print("fileFormat : " + getFileNameSuffix(fileName));
     50                 throw new RuntimeException("nonsupport file format, please check input fileName again");
     51             }
     52         }
     53     }
     54     /**
     55      * getXlsSheetItem : 读取xls格式文件的所有sheet表的所有列名集合。
     56      *
     57      * @param fileName 文件名
     58      *                  默认表格格式规范,列名全部为字符串。
     59      */
     60     private static List<Map<String, Object>> getXlsSheetItem(String fileName) {
     61         TableObject tableObject = new TableObject(fileName).invoke();
     62         HSSFWorkbook book = tableObject.getHssfBook();
     63         List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
     64         for (int i = 0; i < tableObject.getSheet_number(); i++) {
     65             Sheet sheet = book.getSheetAt(i);
     66             if (ifSheetNullOrEmpty(sheet)) continue;
     67             Row row = sheet.getRow(0);
     68             if (ifRowNullOrEmpty(row)) continue;
     69             result.add(packageColsWithSheetName(book, i, row));
     70         }
     71         return result;
     72     }
     73     /**
     74      * getXlsxSheetItem : 读取xlsx格式文件的所有sheet表的所有列名集合。
     75      *
     76      * @param fileName 文件名
     77      *                  默认表格格式规范,列名全部为字符串。
     78      * 方法还可以进一步化简,可参考 getExcelData 进一步整合。
     79      */
     80     private static List<Map<String, Object>> getXlsxSheetItem(String fileName) {
     81         TableObject tableObject = new TableObject(fileName).invoke();
     82         XSSFWorkbook book = tableObject.getXssfBook();
     83         List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
     84         for (int i = 0; i < tableObject.getSheet_number(); i++) {
     85             Sheet sheet = book.getSheetAt(i);
     86             if (ifSheetNullOrEmpty(sheet)) continue;
     87             Row row = sheet.getRow(0);
     88             if (ifRowNullOrEmpty(row)) continue;
     89             result.add(packageColsWithSheetName(book, i, row));
     90         }
     91         return result;
     92     }
     93     private static Map<String, Object> packageColsWithSheetName(Workbook book, int i, Row row) {
     94         Map<String, Object> map = new HashMap<String, Object>();
     95         String sheet_name = book.getSheetName(i);
     96         List<String> cols = getCols(row);
     97         map.put("sheet_name", sheet_name);
     98         map.put("cols", cols);
     99         return map;
    100     }
    101     private static List<String> getCols(Row row) {
    102         List<String> cols = new ArrayList<String>();
    103         System.out.println(row.getLastCellNum());
    104         for (int j = 0; j < row.getLastCellNum(); j++) {
    105             Object obj = row.getCell(j);
    106             cols.add(obj == null ? "" : obj.toString());
    107         }
    108         return cols;
    109     }
    110     private static boolean ifRowNullOrEmpty(Row row) {
    111         if (row == null || row.getLastCellNum() == 0 || row.getCell(0) == null) {
    112             return true;
    113         }
    114         return false;
    115     }
    116     private static boolean ifSheetNullOrEmpty(Sheet sheet) {
    117         if (sheet == null || sheet.getLastRowNum() == 0) {
    118             return true;
    119         }
    120         return false;
    121     }
    122     /**
    123      * 获取表格的全部数据
    124      * @param fileName 文件名
    125      * @return
    126      * @throws RuntimeException
    127      */
    128     public static Map<String, List<Map<String, Object>>> getTableSheetData(String fileName) throws RuntimeException{
    129         if(!valid(fileName)) {
    130             print("文件名校验失败");
    131             return null;
    132         }
    133         else{
    134             return getExcelData(fileName);
    135         }
    136     }
    137     /**
    138      * 根据文件后缀格式,确定调用哪种Workbook,此处运用了多态,具体的解析操作都用的是接口。
    139      * @param fileName
    140      * @return
    141      */
    142     private static Map<String, List<Map<String, Object>>> getExcelData(String fileName) {
    143         TableObject tableObject = new TableObject(fileName).invoke();
    144         Workbook book = null;
    145         if(validExcelFormat(fileName,SUFFIX_XLS)){
    146             //是excel2003?
    147             book = tableObject.getHssfBook();
    148         }
    149         else if(validExcelFormat(fileName, SUFFIX_XLSX)){
    150             //是excel2007以后
    151             book = tableObject.getXssfBook();
    152         }else{
    153             print("nonsupport file format");
    154             throw new RuntimeException("nonsupport file format, please check input fileName again");
    155         }
    156         Map<String, List<Map<String, Object>>> result = new HashMap<String, List<Map<String, Object>>>();
    157         for (int i = 0; i < tableObject.getSheet_number(); i++) {
    158             List<Map<String, Object>> sheet_data = new ArrayList<Map<String, Object>>();
    159             Sheet sheet = book.getSheetAt(i);
    160             if (ifSheetNullOrEmpty(sheet)) continue;
    161             String sheet_name = book.getSheetName(i);
    162             System.out.println(sheet.getLastRowNum());
    163             for (int j = 1; j <= sheet.getLastRowNum(); j++) {
    164                 Row row = sheet.getRow(j);
    165                 if (ifRowNullOrEmpty(row)) continue;
    166                 Map<String, Object> record = new HashMap<String, Object>();
    167                 Row first = sheet.getRow(0);
    168                 getRowData(row, record, first);
    169                 sheet_data.add(record);
    170             }
    171             result.put(sheet_name, sheet_data);
    172         }
    173         return result;
    174     }
    175     
    176     /**
    177      * 此处有个点要注意,getLastCellNum,下标是从1开始,有多少列,这里就是这个值.而getLastRowNum,下标是从0开始,也就是21行的表格,这里获得的值是20.用户可自行验证.
    178      * @param row 该行记录
    179      * @param record 返回值
    180      * @param first 表头
    181      */
    182     private static void getRowData(Row row, Map<String, Object> record, Row first) {
    183         for (int k = 0; k < row.getLastCellNum(); k++) {
    184             String value;
    185             if (row.getCell(k) == null) {
    186                 value = "";
    187             } else {
    188                 value = parseDate(row.getCell(k));
    189                 if (value.endsWith("00:00:00")) {
    190                     value = value.substring(0, value.lastIndexOf("00:00:00"));
    191                 }
    192             }
    193             record.put(first.getCell(k).toString(), value);
    194         }
    195     }
    196     /**
    197      * 判断单元格格式,转化日期格式,日期在poi里保存的是数字,所以这里要转化一下.
    198      * @param cell 单元格
    199      * @return
    200      */
    201     private static String parseDate(Cell cell) {
    202         String result = "";
    203         switch (cell.getCellType()) {
    204             case Cell.CELL_TYPE_NUMERIC:// 数字类型
    205                 if (DateUtil.isCellDateFormatted(cell)) {// 处理日期格式、时间格式
    206                     SimpleDateFormat sdf = null;
    207                     if (cell.getCellStyle().getDataFormat() == HSSFDataFormat
    208                             .getBuiltinFormat("yyyy/MM/dd")) {
    209                         sdf = new SimpleDateFormat("yyyy/MM/dd");
    210                     } else {// 日期
    211                         sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
    212                     }
    213                     Date date = cell.getDateCellValue();
    214                     result = sdf.format(date);
    215                 } else if (cell.getCellStyle().getDataFormat() == 58) {
    216                     // 处理自定义日期格式:m月d日(通过判断单元格的格式id解决,id的值是58)
    217                     SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    218                     double value = cell.getNumericCellValue();
    219                     Date date = org.apache.poi.ss.usermodel.DateUtil
    220                             .getJavaDate(value);
    221                     result = sdf.format(date);
    222                 } else {
    223                     double value = cell.getNumericCellValue();
    224                     CellStyle style = cell.getCellStyle();
    225                     DecimalFormat format = new DecimalFormat();
    226                     String temp = style.getDataFormatString();
    227                     // 单元格设置成常规
    228                     if (temp.equals("General")) {
    229                         format.applyPattern("#");
    230                     }
    231                     result = format.format(value);
    232                 }
    233                 break;
    234             case Cell.CELL_TYPE_STRING:// String类型
    235                 result = cell.getRichStringCellValue().toString();
    236                 break;
    237             case Cell.CELL_TYPE_BLANK:
    238                 result = "";
    239                 break;
    240             default:
    241                 result = "";
    242                 break;
    243         }
    244         return result;
    245     }
    246     /**
    247      * ifXls:判断是否为 xls 文件
    248      *
    249      * @param fileName 包含后缀的文件名
    250      */
    251     private static boolean validExcelFormat(String fileName,String type) {
    252         if(getOS().contains("win")){
    253             return valid(fileName) && getFileNameSuffix(fileName).equalsIgnoreCase(type);
    254         }else if(getOS().contains("linux")){
    255             return valid(fileName) && getFileNameSuffix(fileName).equals(type);
    256         }else{
    257             print("System OS is not windows or linux");
    258             throw new RuntimeException("System OS is not windows or linux , if you use this class in another sys,please implement yourself");
    259         }
    260     }
    261     private static String getOS() { return System.getProperty("os.name").toLowerCase(); }
    262     private static void print(String msg) {
    263         System.out.println(msg);
    264     }
    265     private static String getFileNameSuffix(String fileName) {
    266         return fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length());
    267     }
    268     /**
    269      * valid :判断文件是否存在
    270      *
    271      * @param fileName fileName 文件名。
    272      *                 区分为文件名为null,或者为空。
    273      */
    274     private static boolean valid(String fileName) {
    275         return !ifNull(fileName) && !ifEmpty(fileName);
    276     }
    277     private static boolean ifEmpty(String fileName) {
    278         if (fileName.equals("")) {
    279             print(NO_FILE_EMPTY);
    280             return true;
    281         }
    282         return false;
    283     }
    284     private static boolean ifNull(String fileName) {
    285         if (fileName == null) {
    286             print(NO_FILE_NULL);
    287             return true;
    288         }
    289         return false;
    290     }
    291     /**
    292      * 将excel文件写出成txt格式
    293      * 此处没有指定输出格式
    294      * @param filePath 输入文件路径
    295      * @param destFilePath 输出文件路径
    296      * @throws Exception
    297      */
    298     public static void writeExcel2Txt(String filePath,String destFilePath) throws Exception {
    299         BufferedWriter bufferedWriter = null;
    300         try {
    301             FileWriter fw = new FileWriter(destFilePath);
    302             bufferedWriter = new BufferedWriter(fw);
    303         } catch (IOException e) {
    304             e.printStackTrace();
    305             return;
    306         }
    307         Map<String, List<Map<String, Object>>> data = getTableSheetData(filePath);
    308         System.out.println(data.toString());
    309         if(data==null){
    310             throw new Exception("解析数据失败");
    311         }
    312         for (int i = 0; i < data.size(); i++) {
    313             try {
    314                 bufferedWriter.write(DataTransformUtil.mapGetKeyList(data).get(i));
    315                 System.out.println(DataTransformUtil.mapGetKeyList(data).get(i));
    316                 bufferedWriter.newLine();
    317                 bufferedWriter.write("===========================================================");
    318                 bufferedWriter.newLine();
    319             } catch (IOException e) {
    320                 e.printStackTrace();
    321             }
    322             System.out.println("==========================================");
    323             List<Map<String, Object>> sheet = data.get(DataTransformUtil.mapGetKeyList(data).get(i));
    324             for (Map map : sheet) {
    325                 System.out.println(map.toString());
    326                 bufferedWriter.write(map.toString());
    327                 bufferedWriter.newLine();
    328             }
    329             System.out.println();
    330             bufferedWriter.newLine();
    331         }
    332         bufferedWriter.flush();
    333         bufferedWriter.close();
    334     }
    335     /***
    336      * writeXlsxFile:将指定数据集生成为excel文件。
    337      *
    338      * @param data 一个包含record的List集合
    339      * @param name 文件名【带后缀】
    340      * @param path 路径名
    341      *             只是简单输出,尚未设置样式,单元格格式也未深究。
    342      */
    343     public static String writeXlsxFile(List<Map<String, Object>> data, String path, String name) {
    344         // 创建Excel的工作书册 Workbook,对应到一个excel文档
    345         Workbook book = null;
    346         if(validExcelFormat(name,SUFFIX_XLS)){
    347             book = new HSSFWorkbook();
    348         }else if(validExcelFormat(name,SUFFIX_XLSX)){
    349             book = new XSSFWorkbook();
    350         }else {
    351             print("nonsupport file format");
    352             throw new RuntimeException("nonsupport file format, please check input fileName again");
    353         }
    354         // 创建Excel的工作sheet,对应到一个excel文档的tab
    355         Sheet sheet = book.createSheet("sheet1");
    356         sheet.setColumnWidth(0, 4000);
    357         sheet.setColumnWidth(1, 3500);
    358         // 创建Excel的sheet的一行
    359         int c = data.get(0).size();
    360         List cols = DataTransformUtil.mapGetKeyList(data.get(0));
    361         Row head = sheet.createRow(0);
    362         for (int j = 0; j < cols.size(); j++) {
    363             Cell cell = head.createCell(j, 1);
    364             cell.setCellValue(cols.get(j).toString());
    365         }
    366         for (int i = 0; i < data.size(); i++) {
    367             Row row = sheet.createRow(i + 1);
    368             for (int j = 0; j < c; j++) {
    369                 Cell cell = row.createCell(j, 1);
    370                 cell.setCellValue(data.get(i).get(cols.get(j).toString()).toString());
    371             }
    372         }
    373         FileOutputStream os = null;
    374         try {
    375             os = new FileOutputStream(new File(new File(path), name));
    376             book.write(os);
    377             os.close();
    378         } catch (Exception e) {
    379             e.printStackTrace();
    380         }
    381         return name ;
    382     }
    383     public static void main(String[] args) throws Exception {
    384 //        String str="[{'name':'zhang3','age':33,'gender':'f'}," +
    385 //                "{'name':'li4','age':44,'gender':'m'}," +
    386 //                "{'name':'wang5','age':55,'gender':'m'}," +
    387 //                "{'name':'wang5','age':55,'gender':'m'}," +
    388 //                "{'name':'wang5','age':55,'gender':'m'}]";
    389 //        List<Map<String,Object>> list=DataTransformUtil.nestJsonArrayString2List(str);
    390 //        System.out.println(list.toString());
    391 //        writeXlsxFile(list,"F:\","test");
    392         long start = System.currentTimeMillis();
    393         System.out.println("start :" + System.currentTimeMillis());
    394        // writeExcel2Txt("F:\152657884.xls","F:\123.txt");
    395         //System.out.println(getTableSheetCol("F:\需要本地开发功能列表.xlsx"));
    396         System.out.println(getTableSheetData("F:\需要本地开发功能列表.xlsx"));
    397     }
    398     /**
    399      * extract method object
    400      */
    401     private static class TableObject {
    402         private String fileName;
    403         private XSSFWorkbook xssfBook;
    404         private HSSFWorkbook hssfBook;
    405         private int sheet_number;
    406         public TableObject(String fileName) {
    407             this.fileName = fileName;
    408         }
    409         public XSSFWorkbook getXssfBook() {
    410             return xssfBook;
    411         }
    412         public HSSFWorkbook getHssfBook() {
    413             return hssfBook;
    414         }
    415         public int getSheet_number() {
    416             return sheet_number;
    417         }
    418         public TableObject invoke() {
    419             xssfBook = null;
    420             hssfBook = null;
    421             sheet_number = 0;
    422             try {
    423                 FileInputStream is = new FileInputStream(new File(fileName));
    424                 if(validExcelFormat(fileName,SUFFIX_XLS)){
    425                     hssfBook = new HSSFWorkbook(is);
    426                     sheet_number = hssfBook.getNumberOfSheets();
    427                 }else if(validExcelFormat(fileName,SUFFIX_XLSX)){
    428                     xssfBook = new XSSFWorkbook(is);
    429                     sheet_number = xssfBook.getNumberOfSheets();
    430                 }
    431             } catch (IOException e) {
    432                 e.printStackTrace();
    433             }
    434             return this;
    435         }
    436     }
    437 }

    发现有个WorkBookFactory的api方法,可以直接拿到对应的HSSFWorkbook或XSSFWorkbook

    所以说,研究清楚api才是正道啊……

    大牛博客传送门:http://blog.csdn.net/lovesomnus/article/details/23843549 

  • 相关阅读:
    资源管理与调度系统-资源管理系统Mesos
    hive介绍及架构设计
    企业微信号注册步骤详解及zabbix报警媒介配置
    zabbix Server 4.0 监控Nginx
    zabbix Server 4.0 监控TCP的12种状态
    资源管理与调度系统-YARN资源隔离及以YARN为核心的生态系统
    资源管理与调度系统-YARN的资源调度器
    资源管理与调度系统-YARN的基本架构与原理
    批处理引擎MapReduce应用案例
    批处理引擎MapReduce内部原理
  • 原文地址:https://www.cnblogs.com/opensesame/p/6064486.html
Copyright © 2020-2023  润新知