poi讀取Excel模板並修改模板內容與動態的增加行


  有時候我們可能遇到相當復雜的excel,比如表頭的合並等操作,一種簡單的方式就是直接代碼合並(浪費時間),另一種就是寫好模板,動態的向模板中增加行和修改指定單元格數據。

1.一個簡單的根據模板sheet動態修改

  原來的excel模板內容如下:

 

 現在的需求是動態的生成生成時間和生成人。並且在第五行開始的數據列表增加5列:

package cn.xm.exam.test;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class DynamicOperateExcelUtils {

    public static void main(String[] args) throws IOException {
        // 讀取源文件
        FileInputStream fis = new FileInputStream("G:/test.xlsx");
        XSSFWorkbook workBook = new XSSFWorkbook(fis);

        // 進行模板的克隆(接下來的操作都是針對克隆后的sheet)
        XSSFSheet sheet = workBook.cloneSheet(0);
        workBook.setSheetName(0, "sheet-0"); // 給sheet命名

        // 讀取指定cell的內容
        XSSFCell nameCell = sheet.getRow(1).getCell(0);
        XSSFCell nameCell2 = sheet.getRow(1).getCell(1);
        System.out.println(nameCell.getStringCellValue());
        System.out.println(nameCell2.getStringCellValue());

        // 替換單元格內容(注意獲取的cell的下標是合並之前的下標)
        replaceCellValue(sheet.getRow(1).getCell(2), "xxxxx時間");
        replaceCellValue(sheet.getRow(2).getCell(2), "xxxxx人");

        // 動態插入數據-增加行
        List<Map<String, Object>> datas = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            Map data = new HashMap<>();
            data.put("name", "name" + i);
            data.put("age", "age" + i);
            data.put("sex", "sex" + i);
            datas.add(data);
        }
        // 插入行
        sheet.shiftRows(4, 4 + datas.size(), datas.size(), true, false);// 第1個參數是指要開始插入的行,第2個參數是結尾行數,第三個參數表示動態添加的行數
        for (int i = 0; i < datas.size(); i++) {
            XSSFRow creRow = sheet.createRow(4 + i);
            creRow.setRowStyle(sheet.getRow(4).getRowStyle());
            creRow.createCell(0).setCellValue(datas.get(i).get("name").toString());
            creRow.createCell(1).setCellValue(datas.get(i).get("age").toString());
            creRow.createCell(2).setCellValue(datas.get(i).get("sex").toString());
        }

        // 輸出為一個新的Excel,也就是動態修改完之后的excel
        String fileName = "test" + System.currentTimeMillis() + ".xlsx";
        OutputStream out = new FileOutputStream("G:" + "/" + fileName);
        workBook.removeSheetAt(0); // 移除workbook中的模板sheet
        workBook.write(out);

        fis.close();
        out.flush();
        out.close();
    }

    /**
     * 替換單元格的內容,單元格的獲取位置是合並單元格之前的位置,也就是下標都是合並之前的下表
     * 
     * @param cell
     *            單元格
     * @param value
     *            需要設置的值
     */
    public static void replaceCellValue(Cell cell, Object value) {
        String val = value != null ? String.valueOf(value) : "";
        cell.setCellValue(val);
    }
}

 結果:

 

   上面需要注意的是:在替換的時候獲取cell的時候獲取的是合並單元格之前的cell位置,在動態增加行的時候行的其實和結束都是包含在內的。

 

 2.  封裝的一個完整的工具類:

  此工具類支持xls和xlsx格式(這也是一種常用的思想,用父類引用接受子類對象),完美的支持excel的操作。而且是單個sheet的模板替換以及追加內容、讀取和設置單個cell的值。

  代碼中依賴的工具包:Slf4j日志包,IOUtils工具包,commons-collections操作集合包。

package cn.xm.exam.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.IOUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
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.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DynamicOperateExcelUtils {

    private static final Logger LOGGER = LoggerFactory.getLogger(DynamicOperateExcelUtils.class);

    private Workbook workBook;
    private Sheet sheet;

    public DynamicOperateExcelUtils(String fileFullPath) {
        this(fileFullPath, null);
    }

    public DynamicOperateExcelUtils(String fileFullPath, String sheetName) {
        // 解決版本問題,HSSFWorkbook是97-03版本的xls版本,XSSFWorkbook是07版本的xlsx
        try {
            workBook = new XSSFWorkbook(new FileInputStream(fileFullPath));
        } catch (Exception e) {
            try {
                workBook = new HSSFWorkbook(new FileInputStream(fileFullPath));
            } catch (Exception e1) {
                LOGGER.error("Excel格式不正確", e1);
                throw new RuntimeException(e1);
            }
        }

        // 進行模板的克隆(接下來的操作都是針對克隆后的sheet)
        sheet = workBook.cloneSheet(0);
        // 移除workbook中的模板sheet
        workBook.removeSheetAt(0);
        // 重命名克隆后的sheet
        workBook.setSheetName(0, sheetName != null ? sheetName : "sheet1");
    }

    public String getCellValue(int rowNum, int colNum) {
        return getCellValue(rowNum, colNum, "");
    }

    /**
     * 根據行號列號獲取值
     * 
     * @param rowNum
     *            行號
     * @param colNum
     *            列號
     * @param defaultValue
     *            默認值
     * @return
     */
    public String getCellValue(int rowNum, int colNum, String defaultValue) {
        Row row = sheet.getRow(rowNum);
        if (row == null) {
            return defaultValue;
        }

        Cell cell = row.getCell(colNum);
        if (cell == null) {
            return defaultValue;
        }

        return getCellValue(cell, defaultValue);
    }

    public String getCellValue(Cell cell) {
        return getCellValue(cell, "");
    }

    /**
     * 讀取cell的值
     * 
     * @param cell
     *            需要讀取的cell
     * @param defaultValue
     *            默認值
     * @return
     */
    public String getCellValue(Cell cell, String defaultValue) {
        if (cell != null) {
            cell.setCellType(cell.CELL_TYPE_STRING);
            return cell.getStringCellValue();
        }

        return defaultValue;
    }

    /**
     * 替換單元格的內容,單元格的獲取位置是合並單元格之前的位置,也就是下標都是合並之前的下表
     * 
     * @param cell
     *            單元格
     * @param value
     *            需要設置的值
     */
    public void replaceCellValue(Cell cell, Object value) {
        String val = value != null ? String.valueOf(value) : "";
        cell.setCellValue(val);
    }

    /**
     * 根據行號,列號進行替換
     * 
     * @param rowNum
     *            行號
     * @param colNum
     *            列號
     * @param value
     *            值
     */
    public void replaceCellValue(int rowNum, int colNum, Object value) {
        Row row = sheet.getRow(rowNum);
        if (row == null) {
            return;
        }

        Cell cell = row.getCell(colNum);
        if (cell == null) {
            return;
        }

        replaceCellValue(cell, value);
    }

    /**
     * 向sheet中添加行,后面的行會向后自動移動
     * 
     * @param startRowIndex
     *            起始行
     * @param datas
     *            數據
     * @param keys
     *            數據中Map對應的key
     */
    public void appendRows(int startRowIndex, List<Map<String, Object>> datas, String[] keys) {
        // 插入行
        sheet.shiftRows(startRowIndex, startRowIndex + datas.size(), datas.size(), true, false);// 第1個參數是指要開始插入的行,第2個參數是結尾行數,第三個參數表示動態添加的行數
        // 向插入的行中動態的填充數據
        for (int i = 0; i < datas.size(); i++) {
            Map<String, Object> data = datas.get(i);
            // 創建行
            Row row = sheet.createRow(startRowIndex + i);
            // 添加單元格
            Cell cell = null;
            for (int j = 0, length_2 = keys.length; j < length_2; j++) {
                String key = keys[j];
                String value = MapUtils.getString(data, key, "");
                cell = row.createCell(j);
                cell.setCellType(Cell.CELL_TYPE_STRING);
                cell.setCellValue(value);
            }
        }

        // 調整列寬
        autoResizeColumn(keys.length);
    }

    public void exportExcel(File file) {
        exportExcel(file.getAbsolutePath());
    }

    public void exportExcel(String fileFullPath) {
        OutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(fileFullPath);
            workBook.write(outputStream);
        } catch (IOException e) {
            LOGGER.error(" exportExcel error", e);
        } finally {
            IOUtils.closeQuietly(outputStream);
        }
    }

    private void autoResizeColumn(int colNumber) {
        // 如果是SXSSFSheet,需要調用trackAllColumnsForAutoSizing方法一次
        if (sheet instanceof SXSSFSheet) {
            SXSSFSheet tmpSheet = (SXSSFSheet) sheet;
            tmpSheet.trackAllColumnsForAutoSizing();
        }

        for (int i = 0; i < colNumber; i++) {
            sheet.autoSizeColumn(i, true);
        }
    }

    public Sheet getSheet() {
        return sheet;
    }

}

 

測試:

原來excel: myExcel.xlsx

 

 代碼:

    public static void main(String[] args) throws IOException {
        DynamicOperateExcelUtils dynamicOperateExcelUtils = new DynamicOperateExcelUtils("F:/myExcel.xlsx");

        // 讀取內容
        String cellValue = dynamicOperateExcelUtils.getCellValue(1, 1);
        System.out.println(cellValue);

        // 替換單元格內容(注意獲取的cell的下標是合並之前的下標)
        dynamicOperateExcelUtils.replaceCellValue(1, 1, "updated");

        // 動態插入數據-增加行
        List<Map<String, Object>> datas = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            Map data = new HashMap<>();
            data.put("name", "name" + i);
            data.put("age", "age" + i);
            data.put("sex", "sex" + i);
            datas.add(data);
        }
        dynamicOperateExcelUtils.appendRows(4, datas, new String[] { "name", "age", "sex" });

        dynamicOperateExcelUtils.exportExcel(new File("F:/myExcel2.xlsx"));
    }

結果:

什么鬼

myExcel2.xlsx

 

補充:有的POI版本如果第index行沒有任何數據,直接getRow(index)的時候會報錯,所以有可能需要先創建行。(這個問題對不同的POI版本情況不一樣)

 

補充:有時候遇到合並的單元格采用上面自動調整列寬不生效,解決辦法:

sheet.autoSizeColumn(i, true);

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM