• 自己封装的poi操作Excel工具类


    自己封装的poi操作Excel工具类

           在上一篇文章使用poi读写Excel》中分享了一下poi操作Excel的简单示例,这次要分享一下我封装的一个Excel操作的工具类。

     

           该工具类主要完成的功能是:读取Excel、汇总Excel的功能。在读取时,可以设定开始和结束读取的位置、设定是否读取多个sheet、设定读取那个或者那些sheet等。在汇总时,如设定是否覆盖目标文件、设定是否比较检查重复内容、设定检查重复的列索引等功能。具体来演示一下吧:

     

           工具类源码:

    1. package com.tgb.ccl.excel.util;  
    2.   
    3. import java.io.File;  
    4. import java.io.FileInputStream;  
    5. import java.io.FileOutputStream;  
    6. import java.io.IOException;  
    7. import java.util.ArrayList;  
    8. import java.util.List;  
    9.   
    10. import org.apache.poi.hssf.usermodel.HSSFWorkbook;  
    11. import org.apache.poi.ss.usermodel.Cell;  
    12. import org.apache.poi.ss.usermodel.CellStyle;  
    13. import org.apache.poi.ss.usermodel.Row;  
    14. import org.apache.poi.ss.usermodel.Sheet;  
    15. import org.apache.poi.ss.usermodel.Workbook;  
    16. import org.apache.poi.ss.util.CellRangeAddress;  
    17. import org.apache.poi.xssf.usermodel.XSSFWorkbook;  
    18.   
    19. /** 
    20.  * Excel文件操作工具类,包括读、写、合并等功能 
    21.  *  
    22.  * @author  : 龙轩 
    23.  * @group   : tgb8 
    24.  * @Version : 1.00 
    25.  * @Date    : 2014-10-29 上午12:40:44 
    26.  */  
    27. public class ExcelUtil {  
    28.       
    29.     //%%%%%%%%-------常量部分 开始----------%%%%%%%%%   
    30.     /** 
    31.      * 默认的开始读取的行位置为第一行(索引值为0) 
    32.      */  
    33.     private final static int READ_START_POS = 0;  
    34.       
    35.     /** 
    36.      * 默认结束读取的行位置为最后一行(索引值=0,用负数来表示倒数第n行) 
    37.      */  
    38.     private final static int READ_END_POS = 0;  
    39.       
    40.     /** 
    41.      * 默认Excel内容的开始比较列位置为第一列(索引值为0) 
    42.      */  
    43.     private final static int COMPARE_POS = 0;  
    44.       
    45.     /** 
    46.      * 默认多文件合并的时需要做内容比较(相同的内容不重复出现) 
    47.      */  
    48.     private final static boolean NEED_COMPARE = true;  
    49.       
    50.     /** 
    51.      * 默认多文件合并的新文件遇到名称重复时,进行覆盖 
    52.      */  
    53.     private final static boolean NEED_OVERWRITE = true;  
    54.       
    55.     /** 
    56.      * 默认只操作一个sheet 
    57.      */  
    58.     private final static boolean ONLY_ONE_SHEET = true;  
    59.       
    60.     /** 
    61.      * 默认读取第一个sheet中(只有当ONLY_ONE_SHEET = true时有效) 
    62.      */  
    63.     private final static int SELECTED_SHEET = 0;  
    64.       
    65.     /** 
    66.      * 默认从第一个sheet开始读取(索引值为0) 
    67.      */  
    68.     private final static int READ_START_SHEET= 0;  
    69.       
    70.     /** 
    71.      * 默认在最后一个sheet结束读取(索引值=0,用负数来表示倒数第n行) 
    72.      */  
    73.     private final static int READ_END_SHEET = 0;  
    74.       
    75.     /** 
    76.      * 默认打印各种信息 
    77.      */  
    78.     private final static boolean PRINT_MSG = true;  
    79.       
    80.     //%%%%%%%%-------常量部分 结束----------%%%%%%%%%   
    81.       
    82.   
    83.     //%%%%%%%%-------字段部分 开始----------%%%%%%%%%   
    84.     /** 
    85.      * Excel文件路径 
    86.      */  
    87.     private String excelPath = "data.xlsx";  
    88.   
    89.     /** 
    90.      * 设定开始读取的位置,默认为0 
    91.      */  
    92.     private int startReadPos = READ_START_POS;  
    93.   
    94.     /** 
    95.      * 设定结束读取的位置,默认为0,用负数来表示倒数第n行 
    96.      */  
    97.     private int endReadPos = READ_END_POS;  
    98.       
    99.     /** 
    100.      * 设定开始比较的列位置,默认为0 
    101.      */  
    102.     private int comparePos = COMPARE_POS;  
    103.   
    104.     /** 
    105.      *  设定汇总的文件是否需要替换,默认为true 
    106.      */  
    107.     private boolean isOverWrite = NEED_OVERWRITE;  
    108.       
    109.     /** 
    110.      *  设定是否需要比较,默认为true(仅当不覆写目标内容是有效,即isOverWrite=false时有效) 
    111.      */  
    112.     private boolean isNeedCompare = NEED_COMPARE;  
    113.       
    114.     /** 
    115.      * 设定是否只操作第一个sheet 
    116.      */  
    117.     private boolean onlyReadOneSheet = ONLY_ONE_SHEET;  
    118.       
    119.     /** 
    120.      * 设定操作的sheet在索引值 
    121.      */  
    122.     private int selectedSheetIdx =SELECTED_SHEET;  
    123.       
    124.     /** 
    125.      * 设定操作的sheet的名称 
    126.      */  
    127.     private String selectedSheetName = "";  
    128.       
    129.     /** 
    130.      * 设定开始读取的sheet,默认为0 
    131.      */  
    132.     private int startSheetIdx = READ_START_SHEET;  
    133.   
    134.     /** 
    135.      * 设定结束读取的sheet,默认为0,用负数来表示倒数第n行     
    136.      */  
    137.     private int endSheetIdx = READ_END_SHEET;  
    138.       
    139.     /** 
    140.      * 设定是否打印消息 
    141.      */  
    142.     private boolean printMsg = PRINT_MSG;  
    143.       
    144.       
    145.     //%%%%%%%%-------字段部分 结束----------%%%%%%%%%   
    146.       
    147.   
    148.     public static void main(String[] args) {  
    149.         ExcelUtil eu = new ExcelUtil();  
    150.           
    151.         //从第一行开始读取   
    152.         eu.setStartReadPos(1);  
    153.           
    154.         String src_xlspath = "D:\2.xls";  
    155.         String dist_xlsPath = "D:\1.xls";  
    156.         List<Row> rowList;  
    157.         try {  
    158.             rowList = eu.readExcel(src_xlspath);  
    159.             //eu.writeExcel_xls(rowList, src_xlspath, dist_xlsPath);   
    160.         } catch (IOException e) {  
    161.             e.printStackTrace();  
    162.         }  
    163.     }  
    164.       
    165.     public ExcelUtil(){  
    166.           
    167.     }  
    168.       
    169.     public ExcelUtil(String excelPath){  
    170.         this.excelPath = excelPath;  
    171.     }  
    172.       
    173.     /** 
    174.      * 还原设定(其实是重新new一个新的对象并返回) 
    175.      * @return 
    176.      */  
    177.     public ExcelUtil RestoreSettings(){  
    178.         ExcelUtil instance = new  ExcelUtil(this.excelPath);  
    179.         return instance;  
    180.     }  
    181.       
    182.     /** 
    183.      * 自动根据文件扩展名,调用对应的读取方法 
    184.      *  
    185.      * @Title: writeExcel 
    186.      * @Date : 2014-9-11 下午01:50:38 
    187.      * @param xlsPath 
    188.      * @throws IOException 
    189.      */  
    190.     public List<Row> readExcel() throws IOException{  
    191.         return readExcel(this.excelPath);  
    192.     }  
    193.   
    194.     /** 
    195.      * 自动根据文件扩展名,调用对应的读取方法 
    196.      *  
    197.      * @Title: writeExcel 
    198.      * @Date : 2014-9-11 下午01:50:38 
    199.      * @param xlsPath 
    200.      * @throws IOException 
    201.      */  
    202.     public List<Row> readExcel(String xlsPath) throws IOException{  
    203.           
    204.         //扩展名为空时,   
    205.         if (xlsPath.equals("")){  
    206.             throw new IOException("文件路径不能为空!");  
    207.         }else{  
    208.             File file = new File(xlsPath);  
    209.             if(!file.exists()){  
    210.                 throw new IOException("文件不存在!");  
    211.             }  
    212.         }  
    213.           
    214.         //获取扩展名   
    215.         String ext = xlsPath.substring(xlsPath.lastIndexOf(".")+1);  
    216.           
    217.         try {  
    218.               
    219.             if("xls".equals(ext)){              //使用xls方式读取   
    220.                 return readExcel_xls(xlsPath);  
    221.             }else if("xlsx".equals(ext)){       //使用xlsx方式读取   
    222.                 return readExcel_xlsx(xlsPath);  
    223.             }else{                                  //依次尝试xls、xlsx方式读取   
    224.                 out("您要操作的文件没有扩展名,正在尝试以xls方式读取...");  
    225.                 try{  
    226.                     return readExcel_xls(xlsPath);  
    227.                 } catch (IOException e1) {  
    228.                     out("尝试以xls方式读取,结果失败!,正在尝试以xlsx方式读取...");  
    229.                     try{  
    230.                         return readExcel_xlsx(xlsPath);  
    231.                     } catch (IOException e2) {  
    232.                         out("尝试以xls方式读取,结果失败! 请您确保您的文件是Excel文件,并且无损,然后再试。");  
    233.                         throw e2;  
    234.                     }  
    235.                 }  
    236.             }  
    237.         } catch (IOException e) {  
    238.             throw e;  
    239.         }  
    240.     }  
    241.       
    242.     /** 
    243.      * 自动根据文件扩展名,调用对应的写入方法 
    244.      *  
    245.      * @Title: writeExcel 
    246.      * @Date : 2014-9-11 下午01:50:38 
    247.      * @param rowList 
    248.      * @throws IOException 
    249.      */  
    250.     public void writeExcel(List<Row> rowList) throws IOException{  
    251.         writeExcel(rowList,excelPath);  
    252.     }  
    253.       
    254.     /** 
    255.      * 自动根据文件扩展名,调用对应的写入方法 
    256.      *  
    257.      * @Title: writeExcel 
    258.      * @Date : 2014-9-11 下午01:50:38 
    259.      * @param rowList 
    260.      * @param xlsPath 
    261.      * @throws IOException 
    262.      */  
    263.     public void writeExcel(List<Row> rowList, String xlsPath) throws IOException {  
    264.   
    265.         //扩展名为空时,   
    266.         if (xlsPath.equals("")){  
    267.             throw new IOException("文件路径不能为空!");  
    268.         }  
    269.           
    270.         //获取扩展名   
    271.         String ext = xlsPath.substring(xlsPath.lastIndexOf(".")+1);  
    272.           
    273.         try {  
    274.               
    275.             if("xls".equals(ext)){              //使用xls方式写入   
    276.                 writeExcel_xls(rowList,xlsPath);  
    277.             }else if("xlsx".equals(ext)){       //使用xlsx方式写入   
    278.                 writeExcel_xlsx(rowList,xlsPath);  
    279.             }else{                                  //依次尝试xls、xlsx方式写入   
    280.                 out("您要操作的文件没有扩展名,正在尝试以xls方式写入...");  
    281.                 try{  
    282.                     writeExcel_xls(rowList,xlsPath);  
    283.                 } catch (IOException e1) {  
    284.                     out("尝试以xls方式写入,结果失败!,正在尝试以xlsx方式读取...");  
    285.                     try{  
    286.                         writeExcel_xlsx(rowList,xlsPath);  
    287.                     } catch (IOException e2) {  
    288.                         out("尝试以xls方式写入,结果失败! 请您确保您的文件是Excel文件,并且无损,然后再试。");  
    289.                         throw e2;  
    290.                     }  
    291.                 }  
    292.             }  
    293.         } catch (IOException e) {  
    294.             throw e;  
    295.         }  
    296.     }  
    297.       
    298.     /** 
    299.      * 修改Excel(97-03版,xls格式) 
    300.      *  
    301.      * @Title: writeExcel_xls 
    302.      * @Date : 2014-9-11 下午01:50:38 
    303.      * @param rowList 
    304.      * @param dist_xlsPath 
    305.      * @throws IOException 
    306.      */  
    307.     public void writeExcel_xls(List<Row> rowList, String dist_xlsPath) throws IOException {  
    308.         writeExcel_xls(rowList, excelPath,dist_xlsPath);  
    309.     }  
    310.   
    311.     /** 
    312.      * 修改Excel(97-03版,xls格式) 
    313.      *  
    314.      * @Title: writeExcel_xls 
    315.      * @Date : 2014-9-11 下午01:50:38 
    316.      * @param rowList 
    317.      * @param src_xlsPath 
    318.      * @param dist_xlsPath 
    319.      * @throws IOException 
    320.      */  
    321.     public void writeExcel_xls(List<Row> rowList, String src_xlsPath, String dist_xlsPath) throws IOException {  
    322.   
    323.         // 判断文件路径是否为空   
    324.         if (dist_xlsPath == null || dist_xlsPath.equals("")) {  
    325.             out("文件路径不能为空");  
    326.             throw new IOException("文件路径不能为空");  
    327.         }  
    328.         // 判断文件路径是否为空   
    329.         if (src_xlsPath == null || src_xlsPath.equals("")) {  
    330.             out("文件路径不能为空");  
    331.             throw new IOException("文件路径不能为空");  
    332.         }  
    333.   
    334.         // 判断列表是否有数据,如果没有数据,则返回   
    335.         if (rowList == null || rowList.size() == 0) {  
    336.             out("文档为空");  
    337.             return;  
    338.         }  
    339.   
    340.         try {  
    341.             HSSFWorkbook wb = null;  
    342.   
    343.             // 判断文件是否存在   
    344.             File file = new File(dist_xlsPath);  
    345.             if (file.exists()) {  
    346.                 // 如果复写,则删除后   
    347.                 if (isOverWrite) {  
    348.                     file.delete();  
    349.                     // 如果文件不存在,则创建一个新的Excel   
    350.                     // wb = new HSSFWorkbook();   
    351.                     // wb.createSheet("Sheet1");   
    352.                     wb = new HSSFWorkbook(new FileInputStream(src_xlsPath));  
    353.                 } else {  
    354.                     // 如果文件存在,则读取Excel   
    355.                     wb = new HSSFWorkbook(new FileInputStream(file));  
    356.                 }  
    357.             } else {  
    358.                 // 如果文件不存在,则创建一个新的Excel   
    359.                 // wb = new HSSFWorkbook();   
    360.                 // wb.createSheet("Sheet1");   
    361.                 wb = new HSSFWorkbook(new FileInputStream(src_xlsPath));  
    362.             }  
    363.   
    364.             // 将rowlist的内容写到Excel中   
    365.             writeExcel(wb, rowList, dist_xlsPath);  
    366.   
    367.         } catch (IOException e) {  
    368.             e.printStackTrace();  
    369.         }  
    370.     }  
    371.   
    372.     /** 
    373.      * 修改Excel(97-03版,xls格式) 
    374.      *  
    375.      * @Title: writeExcel_xls 
    376.      * @Date : 2014-9-11 下午01:50:38 
    377.      * @param rowList 
    378.      * @param dist_xlsPath 
    379.      * @throws IOException 
    380.      */  
    381.     public void writeExcel_xlsx(List<Row> rowList, String dist_xlsPath) throws IOException {  
    382.         writeExcel_xls(rowList, excelPath , dist_xlsPath);  
    383.     }  
    384.   
    385.     /** 
    386.      * 修改Excel(2007版,xlsx格式) 
    387.      *  
    388.      * @Title: writeExcel_xlsx 
    389.      * @Date : 2014-9-11 下午01:50:38 
    390.      * @param rowList 
    391.      * @param xlsPath 
    392.      * @throws IOException 
    393.      */  
    394.     public void writeExcel_xlsx(List<Row> rowList, String src_xlsPath, String dist_xlsPath) throws IOException {  
    395.   
    396.         // 判断文件路径是否为空   
    397.         if (dist_xlsPath == null || dist_xlsPath.equals("")) {  
    398.             out("文件路径不能为空");  
    399.             throw new IOException("文件路径不能为空");  
    400.         }  
    401.         // 判断文件路径是否为空   
    402.         if (src_xlsPath == null || src_xlsPath.equals("")) {  
    403.             out("文件路径不能为空");  
    404.             throw new IOException("文件路径不能为空");  
    405.         }  
    406.   
    407.         // 判断列表是否有数据,如果没有数据,则返回   
    408.         if (rowList == null || rowList.size() == 0) {  
    409.             out("文档为空");  
    410.             return;  
    411.         }  
    412.   
    413.         try {  
    414.             // 读取文档   
    415.             XSSFWorkbook wb = null;  
    416.   
    417.             // 判断文件是否存在   
    418.             File file = new File(dist_xlsPath);  
    419.             if (file.exists()) {  
    420.                 // 如果复写,则删除后   
    421.                 if (isOverWrite) {  
    422.                     file.delete();  
    423.                     // 如果文件不存在,则创建一个新的Excel   
    424.                     // wb = new XSSFWorkbook();   
    425.                     // wb.createSheet("Sheet1");   
    426.                     wb = new XSSFWorkbook(new FileInputStream(src_xlsPath));  
    427.                 } else {  
    428.                     // 如果文件存在,则读取Excel   
    429.                     wb = new XSSFWorkbook(new FileInputStream(file));  
    430.                 }  
    431.             } else {  
    432.                 // 如果文件不存在,则创建一个新的Excel   
    433.                 // wb = new XSSFWorkbook();   
    434.                 // wb.createSheet("Sheet1");   
    435.                 wb = new XSSFWorkbook(new FileInputStream(src_xlsPath));  
    436.             }  
    437.             // 将rowlist的内容添加到Excel中   
    438.             writeExcel(wb, rowList, dist_xlsPath);  
    439.   
    440.         } catch (IOException e) {  
    441.             e.printStackTrace();  
    442.         }  
    443.     }  
    444.   
    445.     /** 
    446.      * //读取Excel 2007版,xlsx格式 
    447.      *  
    448.      * @Title: readExcel_xlsx 
    449.      * @Date : 2014-9-11 上午11:43:11 
    450.      * @return 
    451.      * @throws IOException 
    452.      */  
    453.     public List<Row> readExcel_xlsx() throws IOException {  
    454.         return readExcel_xlsx(excelPath);  
    455.     }  
    456.   
    457.     /** 
    458.      * //读取Excel 2007版,xlsx格式 
    459.      *  
    460.      * @Title: readExcel_xlsx 
    461.      * @Date : 2014-9-11 上午11:43:11 
    462.      * @return 
    463.      * @throws Exception 
    464.      */  
    465.     public List<Row> readExcel_xlsx(String xlsPath) throws IOException {  
    466.         // 判断文件是否存在   
    467.         File file = new File(xlsPath);  
    468.         if (!file.exists()) {  
    469.             throw new IOException("文件名为" + file.getName() + "Excel文件不存在!");  
    470.         }  
    471.   
    472.         XSSFWorkbook wb = null;  
    473.         List<Row> rowList = new ArrayList<Row>();  
    474.         try {  
    475.             FileInputStream fis = new FileInputStream(file);  
    476.             // 去读Excel   
    477.             wb = new XSSFWorkbook(fis);  
    478.   
    479.             // 读取Excel 2007版,xlsx格式   
    480.             rowList = readExcel(wb);  
    481.   
    482.         } catch (IOException e) {  
    483.             e.printStackTrace();  
    484.         }  
    485.         return rowList;  
    486.     }  
    487.   
    488.     /*** 
    489.      * 读取Excel(97-03版,xls格式) 
    490.      *  
    491.      * @throws IOException 
    492.      *  
    493.      * @Title: readExcel 
    494.      * @Date : 2014-9-11 上午09:53:21 
    495.      */  
    496.     public List<Row> readExcel_xls() throws IOException {  
    497.         return readExcel_xls(excelPath);  
    498.     }  
    499.   
    500.     /*** 
    501.      * 读取Excel(97-03版,xls格式) 
    502.      *  
    503.      * @throws Exception 
    504.      *  
    505.      * @Title: readExcel 
    506.      * @Date : 2014-9-11 上午09:53:21 
    507.      */  
    508.     public List<Row> readExcel_xls(String xlsPath) throws IOException {  
    509.   
    510.         // 判断文件是否存在   
    511.         File file = new File(xlsPath);  
    512.         if (!file.exists()) {  
    513.             throw new IOException("文件名为" + file.getName() + "Excel文件不存在!");  
    514.         }  
    515.   
    516.         HSSFWorkbook wb = null;// 用于Workbook级的操作,创建、删除Excel   
    517.         List<Row> rowList = new ArrayList<Row>();  
    518.   
    519.         try {  
    520.             // 读取Excel   
    521.             wb = new HSSFWorkbook(new FileInputStream(file));  
    522.   
    523.             // 读取Excel 97-03版,xls格式   
    524.             rowList = readExcel(wb);  
    525.   
    526.         } catch (IOException e) {  
    527.             e.printStackTrace();  
    528.         }  
    529.         return rowList;  
    530.     }  
    531.   
    532.     /*** 
    533.      * 读取单元格的值 
    534.      *  
    535.      * @Title: getCellValue 
    536.      * @Date : 2014-9-11 上午10:52:07 
    537.      * @param cell 
    538.      * @return 
    539.      */  
    540.     private String getCellValue(Cell cell) {  
    541.         Object result = "";  
    542.         if (cell != null) {  
    543.             switch (cell.getCellType()) {  
    544.             case Cell.CELL_TYPE_STRING:  
    545.                 result = cell.getStringCellValue();  
    546.                 break;  
    547.             case Cell.CELL_TYPE_NUMERIC:  
    548.                 result = cell.getNumericCellValue();  
    549.                 break;  
    550.             case Cell.CELL_TYPE_BOOLEAN:  
    551.                 result = cell.getBooleanCellValue();  
    552.                 break;  
    553.             case Cell.CELL_TYPE_FORMULA:  
    554.                 result = cell.getCellFormula();  
    555.                 break;  
    556.             case Cell.CELL_TYPE_ERROR:  
    557.                 result = cell.getErrorCellValue();  
    558.                 break;  
    559.             case Cell.CELL_TYPE_BLANK:  
    560.                 break;  
    561.             default:  
    562.                 break;  
    563.             }  
    564.         }  
    565.         return result.toString();  
    566.     }  
    567.   
    568.     /** 
    569.      * 通用读取Excel 
    570.      *  
    571.      * @Title: readExcel 
    572.      * @Date : 2014-9-11 上午11:26:53 
    573.      * @param wb 
    574.      * @return 
    575.      */  
    576.     private List<Row> readExcel(Workbook wb) {  
    577.         List<Row> rowList = new ArrayList<Row>();  
    578.           
    579.         int sheetCount = 1;//需要操作的sheet数量   
    580.           
    581.         Sheet sheet = null;  
    582.         if(onlyReadOneSheet){   //只操作一个sheet   
    583.             // 获取设定操作的sheet(如果设定了名称,按名称查,否则按索引值查)   
    584.             sheet =selectedSheetName.equals("")? wb.getSheetAt(selectedSheetIdx):wb.getSheet(selectedSheetName);  
    585.         }else{                          //操作多个sheet   
    586.             sheetCount = wb.getNumberOfSheets();//获取可以操作的总数量   
    587.         }  
    588.           
    589.         // 获取sheet数目   
    590.         for(int t=startSheetIdx; t<sheetCount+endSheetIdx;t++){  
    591.             // 获取设定操作的sheet   
    592.             if(!onlyReadOneSheet) {  
    593.                 sheet =wb.getSheetAt(t);  
    594.             }  
    595.               
    596.             //获取最后行号   
    597.             int lastRowNum = sheet.getLastRowNum();  
    598.   
    599.             if(lastRowNum>0){    //如果>0,表示有数据   
    600.                 out(" 开始读取名为【"+sheet.getSheetName()+"】的内容:");  
    601.             }  
    602.               
    603.             Row row = null;  
    604.             // 循环读取   
    605.             for (int i = startReadPos; i <= lastRowNum + endReadPos; i++) {  
    606.                 row = sheet.getRow(i);  
    607.                 if (row != null) {  
    608.                     rowList.add(row);  
    609.                     out("第"+(i+1)+"行:",false);  
    610.                      // 获取每一单元格的值   
    611.                      for (int j = 0; j < row.getLastCellNum(); j++) {  
    612.                          String value = getCellValue(row.getCell(j));  
    613.                          if (!value.equals("")) {  
    614.                              out(value + " | ",false);  
    615.                          }  
    616.                      }  
    617.                      out("");  
    618.                 }  
    619.             }  
    620.         }  
    621.         return rowList;  
    622.     }  
    623.   
    624.     /** 
    625.      * 修改Excel,并另存为 
    626.      *  
    627.      * @Title: WriteExcel 
    628.      * @Date : 2014-9-11 下午01:33:59 
    629.      * @param wb 
    630.      * @param rowList 
    631.      * @param xlsPath 
    632.      */  
    633.     private void writeExcel(Workbook wb, List<Row> rowList, String xlsPath) {  
    634.   
    635.         if (wb == null) {  
    636.             out("操作文档不能为空!");  
    637.             return;  
    638.         }  
    639.   
    640.         Sheet sheet = wb.getSheetAt(0);// 修改第一个sheet中的值   
    641.   
    642.         // 如果每次重写,那么则从开始读取的位置写,否则果获取源文件最新的行。   
    643.         int lastRowNum = isOverWrite ? startReadPos : sheet.getLastRowNum() + 1;  
    644.         int t = 0;//记录最新添加的行数   
    645.         out("要添加的数据总条数为:"+rowList.size());  
    646.         for (Row row : rowList) {  
    647.             if (row == null) continue;  
    648.             // 判断是否已经存在该数据   
    649.             int pos = findInExcel(sheet, row);  
    650.   
    651.             Row r = null;// 如果数据行已经存在,则获取后重写,否则自动创建新行。   
    652.             if (pos >= 0) {  
    653.                 sheet.removeRow(sheet.getRow(pos));  
    654.                 r = sheet.createRow(pos);  
    655.             } else {  
    656.                 r = sheet.createRow(lastRowNum + t++);  
    657.             }  
    658.               
    659.             //用于设定单元格样式   
    660.             CellStyle newstyle = wb.createCellStyle();  
    661.               
    662.             //循环为新行创建单元格   
    663.             for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {  
    664.                 Cell cell = r.createCell(i);// 获取数据类型   
    665.                 cell.setCellValue(getCellValue(row.getCell(i)));// 复制单元格的值到新的单元格   
    666.                 // cell.setCellStyle(row.getCell(i).getCellStyle());//出错   
    667.                 if (row.getCell(i) == null) continue;  
    668.                 copyCellStyle(row.getCell(i).getCellStyle(), newstyle); // 获取原来的单元格样式   
    669.                 cell.setCellStyle(newstyle);// 设置样式   
    670.                 // sheet.autoSizeColumn(i);//自动跳转列宽度   
    671.             }  
    672.         }  
    673.         out("其中检测到重复条数为:" + (rowList.size() - t) + " ,追加条数为:"+t);  
    674.           
    675.         // 统一设定合并单元格   
    676.         setMergedRegion(sheet);  
    677.           
    678.         try {  
    679.             // 重新将数据写入Excel中   
    680.             FileOutputStream outputStream = new FileOutputStream(xlsPath);  
    681.             wb.write(outputStream);  
    682.             outputStream.flush();  
    683.             outputStream.close();  
    684.         } catch (Exception e) {  
    685.             out("写入Excel时发生错误! ");  
    686.             e.printStackTrace();  
    687.         }  
    688.     }  
    689.   
    690.     /** 
    691.      * 查找某行数据是否在Excel表中存在,返回行数。 
    692.      *  
    693.      * @Title: findInExcel 
    694.      * @Date : 2014-9-11 下午02:23:12 
    695.      * @param sheet 
    696.      * @param row 
    697.      * @return 
    698.      */  
    699.     private int findInExcel(Sheet sheet, Row row) {  
    700.         int pos = -1;  
    701.   
    702.         try {  
    703.             // 如果覆写目标文件,或者不需要比较,则直接返回   
    704.             if (isOverWrite || !isNeedCompare) {  
    705.                 return pos;  
    706.             }  
    707.             for (int i = startReadPos; i <= sheet.getLastRowNum() + endReadPos; i++) {  
    708.                 Row r = sheet.getRow(i);  
    709.                 if (r != null && row != null) {  
    710.                     String v1 = getCellValue(r.getCell(comparePos));  
    711.                     String v2 = getCellValue(row.getCell(comparePos));  
    712.                     if (v1.equals(v2)) {  
    713.                         pos = i;  
    714.                         break;  
    715.                     }  
    716.                 }  
    717.             }  
    718.         } catch (Exception e) {  
    719.             e.printStackTrace();  
    720.         }  
    721.         return pos;  
    722.     }  
    723.   
    724.     /** 
    725.      * 复制一个单元格样式到目的单元格样式 
    726.      *  
    727.      * @param fromStyle 
    728.      * @param toStyle 
    729.      */  
    730.     public static void copyCellStyle(CellStyle fromStyle, CellStyle toStyle) {  
    731.         toStyle.setAlignment(fromStyle.getAlignment());  
    732.         // 边框和边框颜色   
    733.         toStyle.setBorderBottom(fromStyle.getBorderBottom());  
    734.         toStyle.setBorderLeft(fromStyle.getBorderLeft());  
    735.         toStyle.setBorderRight(fromStyle.getBorderRight());  
    736.         toStyle.setBorderTop(fromStyle.getBorderTop());  
    737.         toStyle.setTopBorderColor(fromStyle.getTopBorderColor());  
    738.         toStyle.setBottomBorderColor(fromStyle.getBottomBorderColor());  
    739.         toStyle.setRightBorderColor(fromStyle.getRightBorderColor());  
    740.         toStyle.setLeftBorderColor(fromStyle.getLeftBorderColor());  
    741.   
    742.         // 背景和前景   
    743.         toStyle.setFillBackgroundColor(fromStyle.getFillBackgroundColor());  
    744.         toStyle.setFillForegroundColor(fromStyle.getFillForegroundColor());  
    745.   
    746.         // 数据格式   
    747.         toStyle.setDataFormat(fromStyle.getDataFormat());  
    748.         toStyle.setFillPattern(fromStyle.getFillPattern());  
    749.         // toStyle.setFont(fromStyle.getFont(null));   
    750.         toStyle.setHidden(fromStyle.getHidden());  
    751.         toStyle.setIndention(fromStyle.getIndention());// 首行缩进   
    752.         toStyle.setLocked(fromStyle.getLocked());  
    753.         toStyle.setRotation(fromStyle.getRotation());// 旋转   
    754.         toStyle.setVerticalAlignment(fromStyle.getVerticalAlignment());  
    755.         toStyle.setWrapText(fromStyle.getWrapText());  
    756.   
    757.     }  
    758.   
    759.     /** 
    760.      * 获取合并单元格的值 
    761.      *  
    762.      * @param sheet 
    763.      * @param row 
    764.      * @param column 
    765.      * @return 
    766.      */  
    767.     public void setMergedRegion(Sheet sheet) {  
    768.         int sheetMergeCount = sheet.getNumMergedRegions();  
    769.   
    770.         for (int i = 0; i < sheetMergeCount; i++) {  
    771.             // 获取合并单元格位置   
    772.             CellRangeAddress ca = sheet.getMergedRegion(i);  
    773.             int firstRow = ca.getFirstRow();  
    774.             if (startReadPos - 1 > firstRow) {// 如果第一个合并单元格格式在正式数据的上面,则跳过。   
    775.                 continue;  
    776.             }  
    777.             int lastRow = ca.getLastRow();  
    778.             int mergeRows = lastRow - firstRow;// 合并的行数   
    779.             int firstColumn = ca.getFirstColumn();  
    780.             int lastColumn = ca.getLastColumn();  
    781.             // 根据合并的单元格位置和大小,调整所有的数据行格式,   
    782.             for (int j = lastRow + 1; j <= sheet.getLastRowNum(); j++) {  
    783.                 // 设定合并单元格   
    784.                 sheet.addMergedRegion(new CellRangeAddress(j, j + mergeRows, firstColumn, lastColumn));  
    785.                 j = j + mergeRows;// 跳过已合并的行   
    786.             }  
    787.   
    788.         }  
    789.     }  
    790.       
    791.   
    792.     /** 
    793.      * 打印消息, 
    794.      * @param msg 消息内容 
    795.      * @param tr 换行 
    796.      */  
    797.     private void out(String msg){  
    798.         if(printMsg){  
    799.             out(msg,true);  
    800.         }  
    801.     }  
    802.     /** 
    803.      * 打印消息, 
    804.      * @param msg 消息内容 
    805.      * @param tr 换行 
    806.      */  
    807.     private void out(String msg,boolean tr){  
    808.         if(printMsg){  
    809.             System.out.print(msg+(tr?" ":""));  
    810.         }  
    811.     }  
    812.   
    813.     public String getExcelPath() {  
    814.         return this.excelPath;  
    815.     }  
    816.   
    817.     public void setExcelPath(String excelPath) {  
    818.         this.excelPath = excelPath;  
    819.     }  
    820.   
    821.     public boolean isNeedCompare() {  
    822.         return isNeedCompare;  
    823.     }  
    824.   
    825.     public void setNeedCompare(boolean isNeedCompare) {  
    826.         this.isNeedCompare = isNeedCompare;  
    827.     }  
    828.   
    829.     public int getComparePos() {  
    830.         return comparePos;  
    831.     }  
    832.   
    833.     public void setComparePos(int comparePos) {  
    834.         this.comparePos = comparePos;  
    835.     }  
    836.   
    837.     public int getStartReadPos() {  
    838.         return startReadPos;  
    839.     }  
    840.   
    841.     public void setStartReadPos(int startReadPos) {  
    842.         this.startReadPos = startReadPos;  
    843.     }  
    844.   
    845.     public int getEndReadPos() {  
    846.         return endReadPos;  
    847.     }  
    848.   
    849.     public void setEndReadPos(int endReadPos) {  
    850.         this.endReadPos = endReadPos;  
    851.     }  
    852.   
    853.     public boolean isOverWrite() {  
    854.         return isOverWrite;  
    855.     }  
    856.   
    857.     public void setOverWrite(boolean isOverWrite) {  
    858.         this.isOverWrite = isOverWrite;  
    859.     }  
    860.   
    861.     public boolean isOnlyReadOneSheet() {  
    862.         return onlyReadOneSheet;  
    863.     }  
    864.   
    865.     public void setOnlyReadOneSheet(boolean onlyReadOneSheet) {  
    866.         this.onlyReadOneSheet = onlyReadOneSheet;  
    867.     }  
    868.   
    869.     public int getSelectedSheetIdx() {  
    870.         return selectedSheetIdx;  
    871.     }  
    872.   
    873.     public void setSelectedSheetIdx(int selectedSheetIdx) {  
    874.         this.selectedSheetIdx = selectedSheetIdx;  
    875.     }  
    876.   
    877.     public String getSelectedSheetName() {  
    878.         return selectedSheetName;  
    879.     }  
    880.   
    881.     public void setSelectedSheetName(String selectedSheetName) {  
    882.         this.selectedSheetName = selectedSheetName;  
    883.     }  
    884.   
    885.     public int getStartSheetIdx() {  
    886.         return startSheetIdx;  
    887.     }  
    888.   
    889.     public void setStartSheetIdx(int startSheetIdx) {  
    890.         this.startSheetIdx = startSheetIdx;  
    891.     }  
    892.   
    893.     public int getEndSheetIdx() {  
    894.         return endSheetIdx;  
    895.     }  
    896.   
    897.     public void setEndSheetIdx(int endSheetIdx) {  
    898.         this.endSheetIdx = endSheetIdx;  
    899.     }  
    900.   
    901.     public boolean isPrintMsg() {  
    902.         return printMsg;  
    903.     }  
    904.   
    905.     public void setPrintMsg(boolean printMsg) {  
    906.         this.printMsg = printMsg;  
    907.     }  
    908. }  
    package com.tgb.ccl.excel.util;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    
    import org.apache.poi.hssf.usermodel.HSSFWorkbook;
    import org.apache.poi.ss.usermodel.Cell;
    import org.apache.poi.ss.usermodel.CellStyle;
    import org.apache.poi.ss.usermodel.Row;
    import org.apache.poi.ss.usermodel.Sheet;
    import org.apache.poi.ss.usermodel.Workbook;
    import org.apache.poi.ss.util.CellRangeAddress;
    import org.apache.poi.xssf.usermodel.XSSFWorkbook;
    
    /**
     * Excel文件操作工具类,包括读、写、合并等功能
     * 
     * @author  : 龙轩
     * @group   : tgb8
     * @Version : 1.00
     * @Date    : 2014-10-29 上午12:40:44
     */
    public class ExcelUtil {
    	
    	//%%%%%%%%-------常量部分 开始----------%%%%%%%%%
    	/**
    	 * 默认的开始读取的行位置为第一行(索引值为0)
    	 */
    	private final static int READ_START_POS = 0;
    	
    	/**
    	 * 默认结束读取的行位置为最后一行(索引值=0,用负数来表示倒数第n行)
    	 */
    	private final static int READ_END_POS = 0;
    	
    	/**
    	 * 默认Excel内容的开始比较列位置为第一列(索引值为0)
    	 */
    	private final static int COMPARE_POS = 0;
    	
    	/**
    	 * 默认多文件合并的时需要做内容比较(相同的内容不重复出现)
    	 */
    	private final static boolean NEED_COMPARE = true;
    	
    	/**
    	 * 默认多文件合并的新文件遇到名称重复时,进行覆盖
    	 */
    	private final static boolean NEED_OVERWRITE = true;
    	
    	/**
    	 * 默认只操作一个sheet
    	 */
    	private final static boolean ONLY_ONE_SHEET = true;
    	
    	/**
    	 * 默认读取第一个sheet中(只有当ONLY_ONE_SHEET = true时有效)
    	 */
    	private final static int SELECTED_SHEET = 0;
    	
    	/**
    	 * 默认从第一个sheet开始读取(索引值为0)
    	 */
    	private final static int READ_START_SHEET= 0;
    	
    	/**
    	 * 默认在最后一个sheet结束读取(索引值=0,用负数来表示倒数第n行)
    	 */
    	private final static int READ_END_SHEET = 0;
    	
    	/**
    	 * 默认打印各种信息
    	 */
    	private final static boolean PRINT_MSG = true;
    	
    	//%%%%%%%%-------常量部分 结束----------%%%%%%%%%
    	
    
    	//%%%%%%%%-------字段部分 开始----------%%%%%%%%%
    	/**
    	 * Excel文件路径
    	 */
    	private String excelPath = "data.xlsx";
    
    	/**
    	 * 设定开始读取的位置,默认为0
    	 */
    	private int startReadPos = READ_START_POS;
    
    	/**
    	 * 设定结束读取的位置,默认为0,用负数来表示倒数第n行
    	 */
    	private int endReadPos = READ_END_POS;
    	
    	/**
    	 * 设定开始比较的列位置,默认为0
    	 */
    	private int comparePos = COMPARE_POS;
    
    	/**
    	 *  设定汇总的文件是否需要替换,默认为true
    	 */
    	private boolean isOverWrite = NEED_OVERWRITE;
    	
    	/**
    	 *  设定是否需要比较,默认为true(仅当不覆写目标内容是有效,即isOverWrite=false时有效)
    	 */
    	private boolean isNeedCompare = NEED_COMPARE;
    	
    	/**
    	 * 设定是否只操作第一个sheet
    	 */
    	private boolean onlyReadOneSheet = ONLY_ONE_SHEET;
    	
    	/**
    	 * 设定操作的sheet在索引值
    	 */
    	private int selectedSheetIdx =SELECTED_SHEET;
    	
    	/**
    	 * 设定操作的sheet的名称
    	 */
    	private String selectedSheetName = "";
    	
    	/**
    	 * 设定开始读取的sheet,默认为0
    	 */
    	private int startSheetIdx = READ_START_SHEET;
    
    	/**
    	 * 设定结束读取的sheet,默认为0,用负数来表示倒数第n行	
    	 */
    	private int endSheetIdx = READ_END_SHEET;
    	
    	/**
    	 * 设定是否打印消息
    	 */
    	private boolean printMsg = PRINT_MSG;
    	
    	
    	//%%%%%%%%-------字段部分 结束----------%%%%%%%%%
    	
    
    	public static void main(String[] args) {
    		ExcelUtil eu = new ExcelUtil();
    		
    		//从第一行开始读取
    		eu.setStartReadPos(1);
    		
    		String src_xlspath = "D:\2.xls";
    		String dist_xlsPath = "D:\1.xls";
    		List<Row> rowList;
    		try {
    			rowList = eu.readExcel(src_xlspath);
    			//eu.writeExcel_xls(rowList, src_xlspath, dist_xlsPath);
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	public ExcelUtil(){
    		
    	}
    	
    	public ExcelUtil(String excelPath){
    		this.excelPath = excelPath;
    	}
    	
    	/**
    	 * 还原设定(其实是重新new一个新的对象并返回)
    	 * @return
    	 */
    	public ExcelUtil RestoreSettings(){
    		ExcelUtil instance = new  ExcelUtil(this.excelPath);
    		return instance;
    	}
    	
    	/**
    	 * 自动根据文件扩展名,调用对应的读取方法
    	 * 
    	 * @Title: writeExcel
    	 * @Date : 2014-9-11 下午01:50:38
    	 * @param xlsPath
    	 * @throws IOException
    	 */
    	public List<Row> readExcel() throws IOException{
    		return readExcel(this.excelPath);
    	}
    
    	/**
    	 * 自动根据文件扩展名,调用对应的读取方法
    	 * 
    	 * @Title: writeExcel
    	 * @Date : 2014-9-11 下午01:50:38
    	 * @param xlsPath
    	 * @throws IOException
    	 */
    	public List<Row> readExcel(String xlsPath) throws IOException{
    		
    		//扩展名为空时,
    		if (xlsPath.equals("")){
    			throw new IOException("文件路径不能为空!");
    		}else{
    			File file = new File(xlsPath);
    			if(!file.exists()){
    				throw new IOException("文件不存在!");
    			}
    		}
    		
    		//获取扩展名
    		String ext = xlsPath.substring(xlsPath.lastIndexOf(".")+1);
    		
    		try {
    			
    			if("xls".equals(ext)){				//使用xls方式读取
    				return readExcel_xls(xlsPath);
    			}else if("xlsx".equals(ext)){		//使用xlsx方式读取
    				return readExcel_xlsx(xlsPath);
    			}else{									//依次尝试xls、xlsx方式读取
    				out("您要操作的文件没有扩展名,正在尝试以xls方式读取...");
    				try{
    					return readExcel_xls(xlsPath);
    				} catch (IOException e1) {
    					out("尝试以xls方式读取,结果失败!,正在尝试以xlsx方式读取...");
    					try{
    						return readExcel_xlsx(xlsPath);
    					} catch (IOException e2) {
    						out("尝试以xls方式读取,结果失败!
    请您确保您的文件是Excel文件,并且无损,然后再试。");
    						throw e2;
    					}
    				}
    			}
    		} catch (IOException e) {
    			throw e;
    		}
    	}
    	
    	/**
    	 * 自动根据文件扩展名,调用对应的写入方法
    	 * 
    	 * @Title: writeExcel
    	 * @Date : 2014-9-11 下午01:50:38
    	 * @param rowList
    	 * @throws IOException
    	 */
    	public void writeExcel(List<Row> rowList) throws IOException{
    		writeExcel(rowList,excelPath);
    	}
    	
    	/**
    	 * 自动根据文件扩展名,调用对应的写入方法
    	 * 
    	 * @Title: writeExcel
    	 * @Date : 2014-9-11 下午01:50:38
    	 * @param rowList
    	 * @param xlsPath
    	 * @throws IOException
    	 */
    	public void writeExcel(List<Row> rowList, String xlsPath) throws IOException {
    
    		//扩展名为空时,
    		if (xlsPath.equals("")){
    			throw new IOException("文件路径不能为空!");
    		}
    		
    		//获取扩展名
    		String ext = xlsPath.substring(xlsPath.lastIndexOf(".")+1);
    		
    		try {
    			
    			if("xls".equals(ext)){				//使用xls方式写入
    				writeExcel_xls(rowList,xlsPath);
    			}else if("xlsx".equals(ext)){		//使用xlsx方式写入
    				writeExcel_xlsx(rowList,xlsPath);
    			}else{									//依次尝试xls、xlsx方式写入
    				out("您要操作的文件没有扩展名,正在尝试以xls方式写入...");
    				try{
    					writeExcel_xls(rowList,xlsPath);
    				} catch (IOException e1) {
    					out("尝试以xls方式写入,结果失败!,正在尝试以xlsx方式读取...");
    					try{
    						writeExcel_xlsx(rowList,xlsPath);
    					} catch (IOException e2) {
    						out("尝试以xls方式写入,结果失败!
    请您确保您的文件是Excel文件,并且无损,然后再试。");
    						throw e2;
    					}
    				}
    			}
    		} catch (IOException e) {
    			throw e;
    		}
    	}
    	
    	/**
    	 * 修改Excel(97-03版,xls格式)
    	 * 
    	 * @Title: writeExcel_xls
    	 * @Date : 2014-9-11 下午01:50:38
    	 * @param rowList
    	 * @param dist_xlsPath
    	 * @throws IOException
    	 */
    	public void writeExcel_xls(List<Row> rowList, String dist_xlsPath) throws IOException {
    		writeExcel_xls(rowList, excelPath,dist_xlsPath);
    	}
    
    	/**
    	 * 修改Excel(97-03版,xls格式)
    	 * 
    	 * @Title: writeExcel_xls
    	 * @Date : 2014-9-11 下午01:50:38
    	 * @param rowList
    	 * @param src_xlsPath
    	 * @param dist_xlsPath
    	 * @throws IOException
    	 */
    	public void writeExcel_xls(List<Row> rowList, String src_xlsPath, String dist_xlsPath) throws IOException {
    
    		// 判断文件路径是否为空
    		if (dist_xlsPath == null || dist_xlsPath.equals("")) {
    			out("文件路径不能为空");
    			throw new IOException("文件路径不能为空");
    		}
    		// 判断文件路径是否为空
    		if (src_xlsPath == null || src_xlsPath.equals("")) {
    			out("文件路径不能为空");
    			throw new IOException("文件路径不能为空");
    		}
    
    		// 判断列表是否有数据,如果没有数据,则返回
    		if (rowList == null || rowList.size() == 0) {
    			out("文档为空");
    			return;
    		}
    
    		try {
    			HSSFWorkbook wb = null;
    
    			// 判断文件是否存在
    			File file = new File(dist_xlsPath);
    			if (file.exists()) {
    				// 如果复写,则删除后
    				if (isOverWrite) {
    					file.delete();
    					// 如果文件不存在,则创建一个新的Excel
    					// wb = new HSSFWorkbook();
    					// wb.createSheet("Sheet1");
    					wb = new HSSFWorkbook(new FileInputStream(src_xlsPath));
    				} else {
    					// 如果文件存在,则读取Excel
    					wb = new HSSFWorkbook(new FileInputStream(file));
    				}
    			} else {
    				// 如果文件不存在,则创建一个新的Excel
    				// wb = new HSSFWorkbook();
    				// wb.createSheet("Sheet1");
    				wb = new HSSFWorkbook(new FileInputStream(src_xlsPath));
    			}
    
    			// 将rowlist的内容写到Excel中
    			writeExcel(wb, rowList, dist_xlsPath);
    
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    
    	/**
    	 * 修改Excel(97-03版,xls格式)
    	 * 
    	 * @Title: writeExcel_xls
    	 * @Date : 2014-9-11 下午01:50:38
    	 * @param rowList
    	 * @param dist_xlsPath
    	 * @throws IOException
    	 */
    	public void writeExcel_xlsx(List<Row> rowList, String dist_xlsPath) throws IOException {
    		writeExcel_xls(rowList, excelPath , dist_xlsPath);
    	}
    
    	/**
    	 * 修改Excel(2007版,xlsx格式)
    	 * 
    	 * @Title: writeExcel_xlsx
    	 * @Date : 2014-9-11 下午01:50:38
    	 * @param rowList
    	 * @param xlsPath
    	 * @throws IOException
    	 */
    	public void writeExcel_xlsx(List<Row> rowList, String src_xlsPath, String dist_xlsPath) throws IOException {
    
    		// 判断文件路径是否为空
    		if (dist_xlsPath == null || dist_xlsPath.equals("")) {
    			out("文件路径不能为空");
    			throw new IOException("文件路径不能为空");
    		}
    		// 判断文件路径是否为空
    		if (src_xlsPath == null || src_xlsPath.equals("")) {
    			out("文件路径不能为空");
    			throw new IOException("文件路径不能为空");
    		}
    
    		// 判断列表是否有数据,如果没有数据,则返回
    		if (rowList == null || rowList.size() == 0) {
    			out("文档为空");
    			return;
    		}
    
    		try {
    			// 读取文档
    			XSSFWorkbook wb = null;
    
    			// 判断文件是否存在
    			File file = new File(dist_xlsPath);
    			if (file.exists()) {
    				// 如果复写,则删除后
    				if (isOverWrite) {
    					file.delete();
    					// 如果文件不存在,则创建一个新的Excel
    					// wb = new XSSFWorkbook();
    					// wb.createSheet("Sheet1");
    					wb = new XSSFWorkbook(new FileInputStream(src_xlsPath));
    				} else {
    					// 如果文件存在,则读取Excel
    					wb = new XSSFWorkbook(new FileInputStream(file));
    				}
    			} else {
    				// 如果文件不存在,则创建一个新的Excel
    				// wb = new XSSFWorkbook();
    				// wb.createSheet("Sheet1");
    				wb = new XSSFWorkbook(new FileInputStream(src_xlsPath));
    			}
    			// 将rowlist的内容添加到Excel中
    			writeExcel(wb, rowList, dist_xlsPath);
    
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    
    	/**
    	 * //读取Excel 2007版,xlsx格式
    	 * 
    	 * @Title: readExcel_xlsx
    	 * @Date : 2014-9-11 上午11:43:11
    	 * @return
    	 * @throws IOException
    	 */
    	public List<Row> readExcel_xlsx() throws IOException {
    		return readExcel_xlsx(excelPath);
    	}
    
    	/**
    	 * //读取Excel 2007版,xlsx格式
    	 * 
    	 * @Title: readExcel_xlsx
    	 * @Date : 2014-9-11 上午11:43:11
    	 * @return
    	 * @throws Exception
    	 */
    	public List<Row> readExcel_xlsx(String xlsPath) throws IOException {
    		// 判断文件是否存在
    		File file = new File(xlsPath);
    		if (!file.exists()) {
    			throw new IOException("文件名为" + file.getName() + "Excel文件不存在!");
    		}
    
    		XSSFWorkbook wb = null;
    		List<Row> rowList = new ArrayList<Row>();
    		try {
    			FileInputStream fis = new FileInputStream(file);
    			// 去读Excel
    			wb = new XSSFWorkbook(fis);
    
    			// 读取Excel 2007版,xlsx格式
    			rowList = readExcel(wb);
    
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    		return rowList;
    	}
    
    	/***
    	 * 读取Excel(97-03版,xls格式)
    	 * 
    	 * @throws IOException
    	 * 
    	 * @Title: readExcel
    	 * @Date : 2014-9-11 上午09:53:21
    	 */
    	public List<Row> readExcel_xls() throws IOException {
    		return readExcel_xls(excelPath);
    	}
    
    	/***
    	 * 读取Excel(97-03版,xls格式)
    	 * 
    	 * @throws Exception
    	 * 
    	 * @Title: readExcel
    	 * @Date : 2014-9-11 上午09:53:21
    	 */
    	public List<Row> readExcel_xls(String xlsPath) throws IOException {
    
    		// 判断文件是否存在
    		File file = new File(xlsPath);
    		if (!file.exists()) {
    			throw new IOException("文件名为" + file.getName() + "Excel文件不存在!");
    		}
    
    		HSSFWorkbook wb = null;// 用于Workbook级的操作,创建、删除Excel
    		List<Row> rowList = new ArrayList<Row>();
    
    		try {
    			// 读取Excel
    			wb = new HSSFWorkbook(new FileInputStream(file));
    
    			// 读取Excel 97-03版,xls格式
    			rowList = readExcel(wb);
    
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    		return rowList;
    	}
    
    	/***
    	 * 读取单元格的值
    	 * 
    	 * @Title: getCellValue
    	 * @Date : 2014-9-11 上午10:52:07
    	 * @param cell
    	 * @return
    	 */
    	private String getCellValue(Cell cell) {
    		Object result = "";
    		if (cell != null) {
    			switch (cell.getCellType()) {
    			case Cell.CELL_TYPE_STRING:
    				result = cell.getStringCellValue();
    				break;
    			case Cell.CELL_TYPE_NUMERIC:
    				result = cell.getNumericCellValue();
    				break;
    			case Cell.CELL_TYPE_BOOLEAN:
    				result = cell.getBooleanCellValue();
    				break;
    			case Cell.CELL_TYPE_FORMULA:
    				result = cell.getCellFormula();
    				break;
    			case Cell.CELL_TYPE_ERROR:
    				result = cell.getErrorCellValue();
    				break;
    			case Cell.CELL_TYPE_BLANK:
    				break;
    			default:
    				break;
    			}
    		}
    		return result.toString();
    	}
    
    	/**
    	 * 通用读取Excel
    	 * 
    	 * @Title: readExcel
    	 * @Date : 2014-9-11 上午11:26:53
    	 * @param wb
    	 * @return
    	 */
    	private List<Row> readExcel(Workbook wb) {
    		List<Row> rowList = new ArrayList<Row>();
    		
    		int sheetCount = 1;//需要操作的sheet数量
    		
    		Sheet sheet = null;
    		if(onlyReadOneSheet){	//只操作一个sheet
    			// 获取设定操作的sheet(如果设定了名称,按名称查,否则按索引值查)
    			sheet =selectedSheetName.equals("")? wb.getSheetAt(selectedSheetIdx):wb.getSheet(selectedSheetName);
    		}else{							//操作多个sheet
    			sheetCount = wb.getNumberOfSheets();//获取可以操作的总数量
    		}
    		
    		// 获取sheet数目
    		for(int t=startSheetIdx; t<sheetCount+endSheetIdx;t++){
    			// 获取设定操作的sheet
    			if(!onlyReadOneSheet) {
    				sheet =wb.getSheetAt(t);
    			}
    			
    			//获取最后行号
    			int lastRowNum = sheet.getLastRowNum();
    
    			if(lastRowNum>0){	//如果>0,表示有数据
    				out("
    开始读取名为【"+sheet.getSheetName()+"】的内容:");
    			}
    			
    			Row row = null;
    			// 循环读取
    			for (int i = startReadPos; i <= lastRowNum + endReadPos; i++) {
    				row = sheet.getRow(i);
    				if (row != null) {
    					rowList.add(row);
    					out("第"+(i+1)+"行:",false);
    					 // 获取每一单元格的值
    					 for (int j = 0; j < row.getLastCellNum(); j++) {
    						 String value = getCellValue(row.getCell(j));
    						 if (!value.equals("")) {
    							 out(value + " | ",false);
    						 }
    					 }
    					 out("");
    				}
    			}
    		}
    		return rowList;
    	}
    
    	/**
    	 * 修改Excel,并另存为
    	 * 
    	 * @Title: WriteExcel
    	 * @Date : 2014-9-11 下午01:33:59
    	 * @param wb
    	 * @param rowList
    	 * @param xlsPath
    	 */
    	private void writeExcel(Workbook wb, List<Row> rowList, String xlsPath) {
    
    		if (wb == null) {
    			out("操作文档不能为空!");
    			return;
    		}
    
    		Sheet sheet = wb.getSheetAt(0);// 修改第一个sheet中的值
    
    		// 如果每次重写,那么则从开始读取的位置写,否则果获取源文件最新的行。
    		int lastRowNum = isOverWrite ? startReadPos : sheet.getLastRowNum() + 1;
    		int t = 0;//记录最新添加的行数
    		out("要添加的数据总条数为:"+rowList.size());
    		for (Row row : rowList) {
    			if (row == null) continue;
    			// 判断是否已经存在该数据
    			int pos = findInExcel(sheet, row);
    
    			Row r = null;// 如果数据行已经存在,则获取后重写,否则自动创建新行。
    			if (pos >= 0) {
    				sheet.removeRow(sheet.getRow(pos));
    				r = sheet.createRow(pos);
    			} else {
    				r = sheet.createRow(lastRowNum + t++);
    			}
    			
    			//用于设定单元格样式
    			CellStyle newstyle = wb.createCellStyle();
    			
    			//循环为新行创建单元格
    			for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
    				Cell cell = r.createCell(i);// 获取数据类型
    				cell.setCellValue(getCellValue(row.getCell(i)));// 复制单元格的值到新的单元格
    				// cell.setCellStyle(row.getCell(i).getCellStyle());//出错
    				if (row.getCell(i) == null) continue;
    				copyCellStyle(row.getCell(i).getCellStyle(), newstyle); // 获取原来的单元格样式
    				cell.setCellStyle(newstyle);// 设置样式
    				// sheet.autoSizeColumn(i);//自动跳转列宽度
    			}
    		}
    		out("其中检测到重复条数为:" + (rowList.size() - t) + " ,追加条数为:"+t);
    		
    		// 统一设定合并单元格
    		setMergedRegion(sheet);
    		
    		try {
    			// 重新将数据写入Excel中
    			FileOutputStream outputStream = new FileOutputStream(xlsPath);
    			wb.write(outputStream);
    			outputStream.flush();
    			outputStream.close();
    		} catch (Exception e) {
    			out("写入Excel时发生错误! ");
    			e.printStackTrace();
    		}
    	}
    
    	/**
    	 * 查找某行数据是否在Excel表中存在,返回行数。
    	 * 
    	 * @Title: findInExcel
    	 * @Date : 2014-9-11 下午02:23:12
    	 * @param sheet
    	 * @param row
    	 * @return
    	 */
    	private int findInExcel(Sheet sheet, Row row) {
    		int pos = -1;
    
    		try {
    			// 如果覆写目标文件,或者不需要比较,则直接返回
    			if (isOverWrite || !isNeedCompare) {
    				return pos;
    			}
    			for (int i = startReadPos; i <= sheet.getLastRowNum() + endReadPos; i++) {
    				Row r = sheet.getRow(i);
    				if (r != null && row != null) {
    					String v1 = getCellValue(r.getCell(comparePos));
    					String v2 = getCellValue(row.getCell(comparePos));
    					if (v1.equals(v2)) {
    						pos = i;
    						break;
    					}
    				}
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		return pos;
    	}
    
    	/**
    	 * 复制一个单元格样式到目的单元格样式
    	 * 
    	 * @param fromStyle
    	 * @param toStyle
    	 */
    	public static void copyCellStyle(CellStyle fromStyle, CellStyle toStyle) {
    		toStyle.setAlignment(fromStyle.getAlignment());
    		// 边框和边框颜色
    		toStyle.setBorderBottom(fromStyle.getBorderBottom());
    		toStyle.setBorderLeft(fromStyle.getBorderLeft());
    		toStyle.setBorderRight(fromStyle.getBorderRight());
    		toStyle.setBorderTop(fromStyle.getBorderTop());
    		toStyle.setTopBorderColor(fromStyle.getTopBorderColor());
    		toStyle.setBottomBorderColor(fromStyle.getBottomBorderColor());
    		toStyle.setRightBorderColor(fromStyle.getRightBorderColor());
    		toStyle.setLeftBorderColor(fromStyle.getLeftBorderColor());
    
    		// 背景和前景
    		toStyle.setFillBackgroundColor(fromStyle.getFillBackgroundColor());
    		toStyle.setFillForegroundColor(fromStyle.getFillForegroundColor());
    
    		// 数据格式
    		toStyle.setDataFormat(fromStyle.getDataFormat());
    		toStyle.setFillPattern(fromStyle.getFillPattern());
    		// toStyle.setFont(fromStyle.getFont(null));
    		toStyle.setHidden(fromStyle.getHidden());
    		toStyle.setIndention(fromStyle.getIndention());// 首行缩进
    		toStyle.setLocked(fromStyle.getLocked());
    		toStyle.setRotation(fromStyle.getRotation());// 旋转
    		toStyle.setVerticalAlignment(fromStyle.getVerticalAlignment());
    		toStyle.setWrapText(fromStyle.getWrapText());
    
    	}
    
    	/**
    	 * 获取合并单元格的值
    	 * 
    	 * @param sheet
    	 * @param row
    	 * @param column
    	 * @return
    	 */
    	public void setMergedRegion(Sheet sheet) {
    		int sheetMergeCount = sheet.getNumMergedRegions();
    
    		for (int i = 0; i < sheetMergeCount; i++) {
    			// 获取合并单元格位置
    			CellRangeAddress ca = sheet.getMergedRegion(i);
    			int firstRow = ca.getFirstRow();
    			if (startReadPos - 1 > firstRow) {// 如果第一个合并单元格格式在正式数据的上面,则跳过。
    				continue;
    			}
    			int lastRow = ca.getLastRow();
    			int mergeRows = lastRow - firstRow;// 合并的行数
    			int firstColumn = ca.getFirstColumn();
    			int lastColumn = ca.getLastColumn();
    			// 根据合并的单元格位置和大小,调整所有的数据行格式,
    			for (int j = lastRow + 1; j <= sheet.getLastRowNum(); j++) {
    				// 设定合并单元格
    				sheet.addMergedRegion(new CellRangeAddress(j, j + mergeRows, firstColumn, lastColumn));
    				j = j + mergeRows;// 跳过已合并的行
    			}
    
    		}
    	}
    	
    
    	/**
    	 * 打印消息,
    	 * @param msg 消息内容
    	 * @param tr 换行
    	 */
    	private void out(String msg){
    		if(printMsg){
    			out(msg,true);
    		}
    	}
    	/**
    	 * 打印消息,
    	 * @param msg 消息内容
    	 * @param tr 换行
    	 */
    	private void out(String msg,boolean tr){
    		if(printMsg){
    			System.out.print(msg+(tr?"
    ":""));
    		}
    	}
    
    	public String getExcelPath() {
    		return this.excelPath;
    	}
    
    	public void setExcelPath(String excelPath) {
    		this.excelPath = excelPath;
    	}
    
    	public boolean isNeedCompare() {
    		return isNeedCompare;
    	}
    
    	public void setNeedCompare(boolean isNeedCompare) {
    		this.isNeedCompare = isNeedCompare;
    	}
    
    	public int getComparePos() {
    		return comparePos;
    	}
    
    	public void setComparePos(int comparePos) {
    		this.comparePos = comparePos;
    	}
    
    	public int getStartReadPos() {
    		return startReadPos;
    	}
    
    	public void setStartReadPos(int startReadPos) {
    		this.startReadPos = startReadPos;
    	}
    
    	public int getEndReadPos() {
    		return endReadPos;
    	}
    
    	public void setEndReadPos(int endReadPos) {
    		this.endReadPos = endReadPos;
    	}
    
    	public boolean isOverWrite() {
    		return isOverWrite;
    	}
    
    	public void setOverWrite(boolean isOverWrite) {
    		this.isOverWrite = isOverWrite;
    	}
    
    	public boolean isOnlyReadOneSheet() {
    		return onlyReadOneSheet;
    	}
    
    	public void setOnlyReadOneSheet(boolean onlyReadOneSheet) {
    		this.onlyReadOneSheet = onlyReadOneSheet;
    	}
    
    	public int getSelectedSheetIdx() {
    		return selectedSheetIdx;
    	}
    
    	public void setSelectedSheetIdx(int selectedSheetIdx) {
    		this.selectedSheetIdx = selectedSheetIdx;
    	}
    
    	public String getSelectedSheetName() {
    		return selectedSheetName;
    	}
    
    	public void setSelectedSheetName(String selectedSheetName) {
    		this.selectedSheetName = selectedSheetName;
    	}
    
    	public int getStartSheetIdx() {
    		return startSheetIdx;
    	}
    
    	public void setStartSheetIdx(int startSheetIdx) {
    		this.startSheetIdx = startSheetIdx;
    	}
    
    	public int getEndSheetIdx() {
    		return endSheetIdx;
    	}
    
    	public void setEndSheetIdx(int endSheetIdx) {
    		this.endSheetIdx = endSheetIdx;
    	}
    
    	public boolean isPrintMsg() {
    		return printMsg;
    	}
    
    	public void setPrintMsg(boolean printMsg) {
    		this.printMsg = printMsg;
    	}
    }
    

           以上就是工具类的全部代码,现在演示一下读取的功能:

    1. public void testRead() {  
    2.     try {  
    3.           
    4.         ExcelUtil eu = new ExcelUtil();  
    5.         eu.setExcelPath("d:\2.xls");  
    6.           
    7.         System.out.println("=======测试Excel 默认 读取========");  
    8.         eu.readExcel();  
    9.           
    10.         System.out.println(" =======测试Excel 从第四行读取,倒数第二行结束========");  
    11.         eu = eu.RestoreSettings();//还原设定   
    12.         eu.setStartReadPos(3);  
    13.         eu.setEndReadPos(-1);  
    14.         eu.readExcel();  
    15.           
    16.         System.out.println(" =======测试Excel 读取第二个sheet========");  
    17.         eu = eu.RestoreSettings();//还原设定   
    18.         eu.setSelectedSheetIdx(1);  
    19.         eu.readExcel();  
    20.           
    21.         System.out.println(" =======测试Excel 读取所有的sheet========");  
    22.         eu = eu.RestoreSettings();//还原设定   
    23.         eu.setOnlyReadOneSheet(false);  
    24.         eu.readExcel();  
    25.           
    26.     } catch (IOException e) {  
    27.         // TODO Auto-generated catch block   
    28.         e.printStackTrace();  
    29.     }  
    30. }  
    	public void testRead() {
    		try {
    			
    			ExcelUtil eu = new ExcelUtil();
    			eu.setExcelPath("d:\2.xls");
    			
    			System.out.println("=======测试Excel 默认 读取========");
    			eu.readExcel();
    			
    			System.out.println("
    =======测试Excel 从第四行读取,倒数第二行结束========");
    			eu = eu.RestoreSettings();//还原设定
    			eu.setStartReadPos(3);
    			eu.setEndReadPos(-1);
    			eu.readExcel();
    			
    			System.out.println("
    =======测试Excel 读取第二个sheet========");
    			eu = eu.RestoreSettings();//还原设定
    			eu.setSelectedSheetIdx(1);
    			eu.readExcel();
    			
    			System.out.println("
    =======测试Excel 读取所有的sheet========");
    			eu = eu.RestoreSettings();//还原设定
    			eu.setOnlyReadOneSheet(false);
    			eu.readExcel();
    			
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
           操作的Excel文件内容如下:
    执行结果如下:


           上面代码的提示和结果已经解释的很清楚了,我就不在这里过多介绍。

     

           现在演示多个Excel的汇总合并功能,把上面的Excel内容合并到另一个Excel表中,这个Excel内容如下:

           测试代码如下:

    1. public void testMerge(){  
    2.     try {  
    3.         ExcelUtil eu1 = new ExcelUtil();//用来读取源xls   
    4.         ExcelUtil eu2 = new ExcelUtil();//用来读取目标xls,用于演示合并结果   
    5.         eu1.setExcelPath("d:\2.xls");  
    6.         eu2.setExcelPath("d:\1.xls");  
    7.           
    8.         System.out.println(" =======修改前,1.xls中的内容========");  
    9.         eu2.readExcel();  
    10.           
    11.         System.out.println(" =======读取源文件2.xls中的内容========");  
    12.         eu1.setStartReadPos(3);  
    13.         //eu1.setOverWrite(false);//是否覆写目标文件(默认覆写)   
    14.         //eu1.setComparePos(1);//设定比较哪一列内容(默认为0,比较第一列内容)   
    15.         //eu1.setNeedCompare(false);//设定是否比较(默认值是true)。只有当不覆盖目标文件时,设置检查重复才有效。   
    16.           
    17.         eu1.writeExcel(eu1.readExcel(), "d:\1.xls");//将读取到的2.xls中的数据合并到1.xls中   
    18.         System.out.println(" =======修改后,1.xls中的内容========");  
    19.         eu2.readExcel();//读取合并后的1.xls的数据   
    20.           
    21.     } catch (IOException e) {  
    22.         // TODO Auto-generated catch block   
    23.         e.printStackTrace();  
    24.     }  
    25. }  
    	public void testMerge(){
    		try {
    			ExcelUtil eu1 = new ExcelUtil();//用来读取源xls
    			ExcelUtil eu2 = new ExcelUtil();//用来读取目标xls,用于演示合并结果
    			eu1.setExcelPath("d:\2.xls");
    			eu2.setExcelPath("d:\1.xls");
    			
    			System.out.println("
    =======修改前,1.xls中的内容========");
    			eu2.readExcel();
    			
    			System.out.println("
    =======读取源文件2.xls中的内容========");
    			eu1.setStartReadPos(3);
    			//eu1.setOverWrite(false);//是否覆写目标文件(默认覆写)
    			//eu1.setComparePos(1);//设定比较哪一列内容(默认为0,比较第一列内容)
    			//eu1.setNeedCompare(false);//设定是否比较(默认值是true)。只有当不覆盖目标文件时,设置检查重复才有效。
    			
    			eu1.writeExcel(eu1.readExcel(), "d:\1.xls");//将读取到的2.xls中的数据合并到1.xls中
    			System.out.println("
    =======修改后,1.xls中的内容========");
    			eu2.readExcel();//读取合并后的1.xls的数据
    			
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
           执行结果如下:
     

           明明有重复的,为什么提示重复为0呢?这是因为默认对目标文件进行了覆写,直接把源文件的数据覆盖到目标文件中。所以只会显示源文件的内容。

           如果把上段测试代码的13、14、15行换成下面的内容,手动还原1.xls,再测试一下:

    1. eu1.setOverWrite(false);//是否覆写目标文件(默认覆写)   
    2. //eu1.setComparePos(1);//设定比较哪一列内容(默认为0,比较第一列内容)   
    3. //eu1.setNeedCompare(false);//设定是否比较(默认值是true)。只有当不覆盖目标文件时,设置检查重复才有效。  
        eu1.setOverWrite(false);//是否覆写目标文件(默认覆写)
        //eu1.setComparePos(1);//设定比较哪一列内容(默认为0,比较第一列内容)
        //eu1.setNeedCompare(false);//设定是否比较(默认值是true)。只有当不覆盖目标文件时,设置检查重复才有效。
    

            执行结果如下:

           这次把覆写给取消了,在合并的时候,默认会比较第一列的数据,存在的则不再追加,所以1.xls中的张三保留了,李四追加到了后面。对比上面的覆盖,可以看到,覆写时跟2.xls的顺序一致(李四在张三前面),而不覆写,则在文档的最后执行追加操作。

     

           我们再次修改测试代码:

    1. eu1.setOverWrite(false);//是否覆写目标文件(默认覆写)   
    2. eu1.setComparePos(1);//设定比较哪一列内容(默认为0,比较第一列内容)   
    3. //eu1.setNeedCompare(false);//设定是否比较(默认值是true)。只有当不覆盖目标文件时,设置检查重复才有效。  
        eu1.setOverWrite(false);//是否覆写目标文件(默认覆写)
        eu1.setComparePos(1);//设定比较哪一列内容(默认为0,比较第一列内容)
        //eu1.setNeedCompare(false);//设定是否比较(默认值是true)。只有当不覆盖目标文件时,设置检查重复才有效。
    

           同时手动把1.xls中的内容修改如下:


           执行结果如下:


           大家可以看到,比较的对象变到的第二列,由于张三所在行的第二列与添加的内容不相同,所以张三被追加到了文档的和面。

     

           最后再次修改测试代码,并且手动还原1.xls为原始状态。

    1. eu1.setOverWrite(false);//是否覆写目标文件(默认覆写)   
    2. //eu1.setComparePos(1);//设定比较哪一列内容(默认为0,比较第一列内容)   
    3. eu1.setNeedCompare(false);//设定是否比较(默认值是true)。只有当不覆盖目标文件时,设置检查重复才有效。  
        eu1.setOverWrite(false);//是否覆写目标文件(默认覆写)
        //eu1.setComparePos(1);//设定比较哪一列内容(默认为0,比较第一列内容)
        eu1.setNeedCompare(false);//设定是否比较(默认值是true)。只有当不覆盖目标文件时,设置检查重复才有效。
    

            执行结果如下:


           这次把覆写和自动比较都取消了,结果就是直接在目标文件的后面进行追加操作。

     

           代码有点多,大家可能看的有点累了,不过写这个工具类消耗了我n多脑细胞,还是希望对大家可以有所帮助。如果有什么问题,欢迎大家批评指正。

  • 相关阅读:
    oldboy_python_bankSystem practice
    【HCIE-RS复习】- PPP
    【HCIE-RS】PPP详解
    【HCIE-RS】考试说明
    【HCIE-RS】杭州考场(个人考试心得体会)
    DataWorks功能实践速览 05——循环与遍历
    Serverless 工程实践 | 零基础上手 Knative 应用
    前后端、多语言、跨云部署,全链路追踪到底有多难?
    多任务多目标CTR预估技术
    开放搜索查询分析服务架构解读
  • 原文地址:https://www.cnblogs.com/firstdream/p/4961046.html
Copyright © 2020-2023  润新知