poi操做excel,複製sheet,複製行,複製單元格,複製style

緣起

最近寫一個程序,須要操做excel,遂稍微深刻了解下poi從操做,java

在須要複製一個sheet的時候,找到三份資料apache

問題

若是在copy一個cell的時候,須要把樣式也copy過去,怎麼辦,會怎樣呢?app

若是像第三份資料中所作,會建立太多的style,而這些style又是重複的 ,毫無心義,數據多了還報錯xss

若是像第二份資料中所作,前提是同一個workbook,也不存在copy style 的問題,只不過是不一樣的cell引用相同的style而已測試

若是像第一份資料所作,他本身都註釋掉了,由於報錯this

如何解決

思路

  • 相同的book中,不須要copy
  • 不一樣的book中,styleTable應該不一致,copy的時候,在目標book中建立,最後返回兩個book中的style index的映射關係

代碼

暫時沒測試過,寫出來就火燒眉毛要分享了。.net

import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Workbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 提供經常使用excel操做<br>
 *     <ul>
 *         <li></li>
 *     </ul>
 */
public class ExcelOperationUtil {
    private static Logger logger = LoggerFactory.getLogger(ExcelOperationUtil.class);
    
    /**
     * 把一個excel中的styleTable複製到另外一個excel中
     * @return StyleMapping 兩個文件中styleTable的映射關係
     * @see StyleMapping
     */
    public static StyleMapping copyCellStyle(Workbook srcBook, Workbook desBook){
        if (null == srcBook || null == desBook) {
            throw new ExcelExecption("源excel 或 目標excel 不存在");
        }
        if (srcBook.equals(desBook)) {
            throw new ExcelExecption("不要使用此方法在同一個文件中copy style");
        }

        logger.debug("src中style number:{}, des中style number:{}", srcBook.getNumCellStyles(), desBook.getNumCellStyles());
        short[] src2des = new short[srcBook.getNumCellStyles()];
        short[] des2src = new short[desBook.getNumCellStyles() + srcBook.getNumCellStyles()];

        for(short i=0;i<srcBook.getNumCellStyles();i++){
            //創建雙向映射
            CellStyle srcStyle = srcBook.getCellStyleAt(i);
            CellStyle desStyle = desBook.createCellStyle();
            src2des[srcStyle.getIndex()] = desStyle.getIndex();
            des2src[desStyle.getIndex()] = srcStyle.getIndex();

            //複製樣式
            desStyle.cloneStyleFrom(srcStyle);
        }

        return new StyleMapping(des2src, src2des);
    }

    /**
     * 存放兩個excel文件中的styleTable的映射關係,以便於在複製表格時,在目標文件中獲取到對應的樣式
     */
    public static class StyleMapping {
        /**
         *
         */
        private short[] des2srcIndexMapping;
        /**
         *
         */
        private short[] src2desIndexMapping;

        /**
         * 不容許其餘類建立此類型對象
         */
        private StyleMapping() {
        }

        private StyleMapping(short[] des2srcIndexMapping, short[] src2desIndexMapping) {
            this.des2srcIndexMapping = des2srcIndexMapping;
            this.src2desIndexMapping = src2desIndexMapping;
        }

        public short srcIndex(short desIndex) {
            if (desIndex < 0 || desIndex >= this.des2srcIndexMapping.length) {
                throw new ExcelExecption("索引越界:源文件styleNum=" + this.des2srcIndexMapping.length + " 訪問位置=" + desIndex);
            }
            return this.des2srcIndexMapping[desIndex];
        }

        /**
         * 根據源文件的style的index,獲取目標文件的style的index
         * @param srcIndex 源excel中style的index
         * @return desIndex 目標excel中style的index
         */
        public short desIndex(short srcIndex) {
            if (srcIndex < 0 || srcIndex >= this.src2desIndexMapping.length) {
                throw new ExcelExecption("索引越界:源文件styleNum=" + this.src2desIndexMapping.length + " 訪問位置=" + srcIndex);
            }

            return this.src2desIndexMapping[srcIndex];
        }
    }

}

完善代碼

工做忙了幾天,終於能夠回頭把這件事結束掉了debug

<dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.17</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.17</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-scratchpad</artifactId>
            <version>3.17</version>
        </dependency>

爲啥貼出依賴?由於我一開始用3.8版本,若是是xls就能夠,若是是xlsx死活不行,最後,換掉版本就能夠了。excel

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Iterator;

/**
 * 提供經常使用excel操做<br>
 *     <ul>
 *         <li></li>
 *     </ul>
 */
public class ExcelOperationUtil {
    private static Logger logger = LoggerFactory.getLogger(ExcelOperationUtil.class);

    /**
     * sheet 複製,複製數據、若是同一個文件,複製樣式,不一樣文件則只複製數據<br/>
     * 若是是同book中複製,建議使用workbook中的cloneSheet()方法<br/>
     *
     * <br/>建議用於 不一樣book間只複製數據
     *
     */
    public static void copySheet(Sheet srcSheet, Sheet desSheet) {
        copySheet(srcSheet, desSheet, true, true, null);
    }

    /**
     * sheet 複製,若是同一個文件,複製樣式,不一樣文件則不復制<br/>
     *
     * <br/>建議用於 同book中,只複製樣式,不復制數據<br/>
     * eg: copySheet(srcSheet, desSheet, false)
     *
     * @param copyValueFlag 控制是否複製數據
     */
    public static void copySheet(Sheet srcSheet, Sheet desSheet, boolean copyValueFlag) {
        copySheet(srcSheet, desSheet, copyValueFlag, true, null);
    }

    /**
     * sheet 複製,複製數據、樣式<br/>
     *
     * <br/>建議用於 不一樣book間複製,同時複製數據和樣式<br/>
     * eg: copySheet(srcSheet, desSheet, mapping)
     *
     * @param mapping 不一樣文件間複製時,若是要複製樣式,必傳,不然不復制樣式
     */
    public static void copySheet(Sheet srcSheet, Sheet desSheet, StyleMapping mapping) {
        copySheet(srcSheet, desSheet, true, true, mapping);
    }

    /**
     * sheet 複製,複製數據<br/>
     *
     *  <br/>建議用於 同book中,只複製數據,不復制樣式<br/>
     *  eg: copySheet(srcSheet, desSheet, false, null)
     *
     * @param srcSheet
     * @param desSheet
     * @param copyStyleFlag
     * @param mapping
     */
    public static void copySheet(Sheet srcSheet, Sheet desSheet, boolean copyStyleFlag, StyleMapping mapping) {
        copySheet(srcSheet, desSheet, true, copyStyleFlag, mapping);
    }

    /**
     * sheet 複製, 靈活控制是否控制數據、樣式<br/>
     *
     * <br/>不建議直接使用
     *
     * @param copyValueFlag 控制是否複製數據
     * @param copyStyleFlag 控制是否複製樣式
     * @param mapping       不一樣book中複製樣式時,必傳
     */
    public static void copySheet(Sheet srcSheet, Sheet desSheet, boolean copyValueFlag, boolean copyStyleFlag, StyleMapping mapping) {
        if (srcSheet.getWorkbook() == desSheet.getWorkbook()) {
            logger.warn("統一workbook內複製sheet建議使用 workbook的cloneSheet方法");
        }

        //合併區域處理
        copyMergedRegion(srcSheet, desSheet);

        //行復制
        Iterator<Row> rowIterator = srcSheet.rowIterator();

        int areadlyColunm = 0;
        while (rowIterator.hasNext()) {
            Row srcRow = rowIterator.next();
            Row desRow = desSheet.createRow(srcRow.getRowNum());
            copyRow(srcRow, desRow, copyValueFlag, copyStyleFlag, mapping);

            //調整列寬(增量調整)
            if (srcRow.getPhysicalNumberOfCells() > areadlyColunm) {
                for (int i = areadlyColunm; i < srcRow.getPhysicalNumberOfCells(); i++) {
                    desSheet.setColumnWidth(i, srcSheet.getColumnWidth(i));
                }
                areadlyColunm = srcRow.getPhysicalNumberOfCells();
            }
        }
    }

    /**
     * 複製行
     */
    public static void copyRow(Row srcRow, Row desRow) {
        copyRow(srcRow, desRow, true, true, null);
    }

    /**
     * 複製行
     */
    public static void copyRow(Row srcRow, Row desRow, boolean copyValueFlag) {
        copyRow(srcRow, desRow, copyValueFlag, true, null);
    }

    /**
     * 複製行
     */
    public static void copyRow(Row srcRow, Row desRow, StyleMapping mapping) {
        copyRow(srcRow, desRow, true, true, mapping);
    }

    /**
     * 複製行
     */
    public static void copyRow(Row srcRow, Row desRow, boolean copyStyleFlag, StyleMapping mapping) {
        copyRow(srcRow, desRow, true, copyStyleFlag, mapping);
    }

    /**
     * 複製行
     */
    public static void copyRow(Row srcRow, Row desRow,boolean copyValueFlag, boolean copyStyleFlag, StyleMapping mapping) {
        Iterator<Cell> it = srcRow.cellIterator();
        while (it.hasNext()) {
            Cell srcCell = it.next();
            Cell desCell = desRow.createCell(srcCell.getColumnIndex());
            copyCell(srcCell, desCell, copyValueFlag, copyStyleFlag, mapping);
        }
    }

    /**
     * 複製區域(合併單元格)
     */
    public static void copyMergedRegion(Sheet srcSheet, Sheet desSheet) {
        int sheetMergerCount = srcSheet.getNumMergedRegions();
        for (int i = 0; i < sheetMergerCount; i++) {
            desSheet.addMergedRegion(srcSheet.getMergedRegion(i));
            CellRangeAddress cellRangeAddress = srcSheet.getMergedRegion(i);
        }
    }

    /**
     * 複製單元格,複製數據,若是同文件,複製樣式,不一樣文件則不復制樣式
     */
    public static void copyCell(Cell srcCell, Cell desCell) {
        copyCell(srcCell, desCell, true, true,null);
    }

    /**
     * 複製單元格, 若是同文件,複製樣式,不一樣文件則不復制樣式
     * @param copyValueFlag 控制是否複製數據
     */
    public static void copyCell(Cell srcCell, Cell desCell, boolean copyValueFlag) {
        copyCell(srcCell, desCell, copyValueFlag, true, null);
    }

    /**
     * 複製單元格,複製數據,複製樣式
     * @param mapping       不一樣文件間複製時,若是要複製樣式,必傳,不然不復制樣式
     */
    public static void copyCell(Cell srcCell, Cell desCell,  StyleMapping mapping) {
        copyCell(srcCell, desCell, true, true, mapping);
    }

    /**
     * 複製單元格,複製數據
     * @param copyStyleFlag 控制是否複製樣式
     * @param mapping       不一樣文件間複製時,若是要複製樣式,必傳,不然不復制樣式
     */
    public static void copyCell(Cell srcCell, Cell desCell, boolean copyStyleFlag, StyleMapping mapping) {
        copyCell(srcCell, desCell, true, copyStyleFlag, mapping);
    }

    /**
     * 複製單元格
     * @param copyValueFlag 控制是否複製單元格的內容
     * @param copyStyleFlag 控制是否複製樣式
     * @param mapping 不一樣文件間複製時,若是須要連帶樣式複製,必傳,不然不復制樣式
     */
    public static void copyCell(Cell srcCell, Cell desCell, boolean copyValueFlag, boolean copyStyleFlag, StyleMapping mapping) {
        Workbook srcBook = srcCell.getSheet().getWorkbook();
        Workbook desBook = desCell.getSheet().getWorkbook();

        //複製樣式
        //若是是同一個excel文件內,連帶樣式一塊兒複製
        if (srcBook == desBook && copyStyleFlag) {
            //同文件,複製引用
            desCell.setCellStyle(srcCell.getCellStyle());
        } else if (copyStyleFlag) {
           //不一樣文件,經過映射關係複製
            if (null != mapping) {
                short desIndex = mapping.desIndex(srcCell.getCellStyle().getIndex());
                desCell.setCellStyle(desBook.getCellStyleAt(desIndex));
            }
        }

        //複製評論
        if (srcCell.getCellComment() != null) {
            desCell.setCellComment(srcCell.getCellComment());
        }

        //複製內容
        desCell.setCellType(srcCell.getCellTypeEnum());

        if (copyValueFlag) {
            switch (srcCell.getCellTypeEnum()) {
                case STRING:
                    desCell.setCellValue(srcCell.getStringCellValue());
                    break;
                case NUMERIC:
                    desCell.setCellValue(srcCell.getNumericCellValue());
                    break;
                case FORMULA:
                    desCell.setCellFormula(srcCell.getCellFormula());
                    break;
                case BOOLEAN:
                    desCell.setCellValue(srcCell.getBooleanCellValue());
                    break;
                case ERROR:
                    desCell.setCellValue(srcCell.getErrorCellValue());
                    break;
                case BLANK:
                    //nothing to do
                    break;
                default:
                    break;
            }
        }

    }


    /**
     * 把一個excel中的styleTable複製到另外一個excel中<br>
     * 若是是同一個excel文件,就不用複製styleTable了
     * @return StyleMapping 兩個文件中styleTable的映射關係
     * @see StyleMapping
     */
    public static StyleMapping copyCellStyle(Workbook srcBook, Workbook desBook){
        if (null == srcBook || null == desBook) {
            throw new ExcelException("源excel 或 目標excel 不存在");
        }
        if (srcBook.equals(desBook)) {
            throw new ExcelException("不要使用此方法在同一個文件中copy style,同一個excel中複製sheet不須要copy Style");
        }
        if ((srcBook instanceof HSSFWorkbook && desBook instanceof XSSFWorkbook) ||
                (srcBook instanceof XSSFWorkbook && desBook instanceof HSSFWorkbook)) {
            throw new ExcelException("不支持在不一樣的版本的excel中複製樣式)");
        }

        logger.debug("src中style number:{}, des中style number:{}", srcBook.getNumCellStyles(), desBook.getNumCellStyles());
        short[] src2des = new short[srcBook.getNumCellStyles()];
        short[] des2src = new short[desBook.getNumCellStyles() + srcBook.getNumCellStyles()];

        for(short i=0;i<srcBook.getNumCellStyles();i++){
            //創建雙向映射
            CellStyle srcStyle = srcBook.getCellStyleAt(i);
            CellStyle desStyle = desBook.createCellStyle();
            src2des[srcStyle.getIndex()] = desStyle.getIndex();
            des2src[desStyle.getIndex()] = srcStyle.getIndex();

            //複製樣式
            desStyle.cloneStyleFrom(srcStyle);
        }


        return new StyleMapping(des2src, src2des);
    }

    /**
     * 存放兩個excel文件中的styleTable的映射關係,以便於在複製表格時,在目標文件中獲取到對應的樣式
     */
    public static class StyleMapping {
        /**
         *
         */
        private short[] des2srcIndexMapping;
        /**
         *
         */
        private short[] src2desIndexMapping;

        /**
         * 不容許其餘類建立此類型對象
         */
        private StyleMapping() {
        }

        public StyleMapping(short[] des2srcIndexMapping, short[] src2desIndexMapping) {
            this.des2srcIndexMapping = des2srcIndexMapping;
            this.src2desIndexMapping = src2desIndexMapping;
        }

        public short srcIndex(short desIndex) {
            if (desIndex < 0 || desIndex >= this.des2srcIndexMapping.length) {
                throw new ExcelException("索引越界:源文件styleNum=" + this.des2srcIndexMapping.length + " 訪問位置=" + desIndex);
            }
            return this.des2srcIndexMapping[desIndex];
        }

        /**
         * 根據源文件的style的index,獲取目標文件的style的index
         * @param srcIndex 源excel中style的index
         * @return desIndex 目標excel中style的index
         */
        public short desIndex(short srcIndex) {
            if (srcIndex < 0 || srcIndex >= this.src2desIndexMapping.length) {
                throw new ExcelException("索引越界:源文件styleNum=" + this.src2desIndexMapping.length + " 訪問位置=" + srcIndex);
            }

            return this.src2desIndexMapping[srcIndex];
        }
    }

}

一個自定義異常類code

public class ExcelException extends RuntimeException {

    public ExcelException() {
    }

    public ExcelException(String message) {
        super(message);
    }

    public ExcelException(String message, Throwable cause) {
        super(message, cause);
    }

    public ExcelException(Throwable cause) {
        super(cause);
    }

    public ExcelException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}
相關文章
相關標籤/搜索