java的Excel合並


   項目中有兩個不同框架組裝成的Excel,現在要對Excel內容合並,組成多sheet的Excel。

  主調用方法:

public ResponseEntity exportExcel(Long id) {
        List<PcOrganization> list = partnerOrganizationService.getList(id);
        List<Long> ids = list.stream().map(PcOrganization::getId).collect(Collectors.toList());
        List<ExportAgentVo> sheet2 = makeSheetDeviceList(ids);
        List<ExportAgentAndCustomerOrderVo> sheet3 = makeSheetCustomerList(ids);
        HashMap<String, Object> map = Maps.newHashMap();
        map.put(ExportEnum.DEVICEdETAIL.getSheetName(), sheet2);
        map.put(ExportEnum.CUSTOMERDETAIL.getSheetName(), sheet3);

        HashMap<String, Class> classMap = Maps.newHashMap();
        classMap.put(ExportEnum.DEVICEdETAIL.getSheetName(), ExportAgentVo.class);
        classMap.put(ExportEnum.CUSTOMERDETAIL.getSheetName(), ExportAgentAndCustomerOrderVo.class);
        LocalDate now = LocalDate.now();
        File datafile = null;
        //File datafile2 = null;
        try {
            datafile = ExcelTools.createDatafile(location, now.toString());
            //datafile2 = ExcelTools.createDatafile(location, "原版");
        } catch (Exception e) {
            throw new BusinessException(ResultCodeEnum.OPERATION_ERR.getCode(), "創建文件失敗");
        }
        Workbook sheets = ExcelTools.exportSheet(map, classMap, datafile);
        PcOrganization organization = list.get(0);
        List<OrgStatisticsExcelVo> statisticsExcelVos = makeSheetOrgStatistics(list);
        //主sheet
        Workbook workBook = StatisticsExcelUtils.createExcelFile2(statisticsExcelVos, organization);
        Iterator<Sheet> sheetIterator = sheets.sheetIterator();
        int hasSheetIndex = workBook.getNumberOfSheets()-1;//主Excel的最后一個sheet的索引
        while(sheetIterator.hasNext()){
            Sheet srcSheet = sheetIterator.next();
            HSSFSheet newSheet1 = (HSSFSheet) workBook.createSheet();
            CopySheetUtil.copySheets(newSheet1, (HSSFSheet) srcSheet, true);
            hasSheetIndex += 1;
            workBook.setSheetName(hasSheetIndex, srcSheet.getSheetName());
        }
        try {
            ExcelTools.writeDatafile(workBook, datafile);
        } catch (Exception e) {
            throw new BusinessException(ResultCodeEnum.OPERATION_ERR.getCode(), "導出失敗");
        }
        return new ResponseEntity(ResultEnum.SUCCESS, null);
    }

  變量sheets是由easypoi生成。變量workbook由原生生成。

附easypoi生成Excel的封裝方法:

import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.util.Assert;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;

public class ExcelTools {

    /**
     * map 即為要導出的數據。map的key為sheet的名稱,value為excel的list數據
     * classMap為easypoi要導出的數據entuty,與第一個參數map要對應。map的長度要與classMap的長度一致。
     * ckassMap的key與第一個參數map的key一致,都是sheet的名稱,value是要解析的注解了@ExcelTagret的class對象。
     * 目的是講第一個參數map的value的list數據解析成classMap的class對象。
     * dataFile為導出的excel的文件全路徑名
     * 功能描述:把同一個表格多個sheet測試結果重新輸出,如果后續增加多個List<Map<String, Object>>對象,需要后面繼續追加
     * @ExcelEntiry sheet表格映射的實體對象
     * @return
     */
    public static Workbook exportSheet(Map  map, Map classMap, File datafile){
        Assert.isTrue(map.size() == classMap.size(), "導出的數據map長度要與對應的對象map長度一致");
        Workbook workBook = null;
        List<Map<String, Object>> sheetsList = new ArrayList<>();
            Iterator<Map.Entry<String, List>> iterator = map.entrySet().iterator();
            while(iterator.hasNext()){
                Map.Entry<String, List> it = iterator.next();
                String key = it.getKey();
                List value = it.getValue();
                Class t = (Class) classMap.get(key);
                // 創建參數對象(用來設定excel得sheet得內容等信息)
                ExportParams exportParams = new ExportParams();
                //添加序號
                //exportParams.setAddIndex(true);
                // 設置sheet得名稱
                exportParams.setSheetName(key);
                // 設置sheet表頭名稱
                exportParams.setTitle(key);
                // 創建sheet使用得map
                Map<String, Object> exportMap = new HashMap<>();
                // title的參數為ExportParams類型,目前僅僅在ExportParams中設置了sheetName
                exportMap.put("title", exportParams);
                // 模版導出對應得實體類型
                exportMap.put("entity", t);
                // sheet中要填充得數據
                exportMap.put("data", value);

                sheetsList.add(exportMap);

            }
        // 執行方法
        workBook = ExcelExportUtil.exportExcel(sheetsList, ExcelType.HSSF);
        return workBook;
    }


    /**
     *  設置文件路徑 && 保證文件對象的正確打開
     * */
    public static File createDatafile(String path, String fileName) throws Exception{
        String resource = path+fileName+".xls";
        //URI uri = new URI(resource);
        File myFile = new File(resource);//創建File對象,參數為String類型,表示目錄名
        //判斷文件是否存在,如不存在則調用createNewFile()創建新目錄,否則跳至異常處理代碼
        if(!myFile.exists()) {
            myFile.createNewFile();
        }
        return myFile;

    }

    public static void writeDatafile(Workbook workBook, File datafile) throws Exception{
        try{
            FileOutputStream fos = new FileOutputStream(datafile);
            workBook.write(fos);
            fos.close();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(workBook != null) {
                try {
                    workBook.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }



}

對easypoi生成的Workbook復制其sheet到主Workbook中。工具類來源於網上copy的,實際運行時發現有bug,改動后可以正常使用:

import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Slf4j
public class CopySheetUtil {
    public CopySheetUtil() {
    }

    public static void  copySheets(HSSFSheet newSheet, HSSFSheet sheet) {
        copySheets(newSheet, sheet, true);
    }

    public static void  copySheets(HSSFSheet newSheet, HSSFSheet sheet,
                                  boolean copyStyle) {
        int maxColumnNum = 0;
        Map<Integer, HSSFCellStyle> styleMap = (copyStyle) ? new HashMap<Integer, HSSFCellStyle>()
                : null;
        Set<CellRangeAddressWrapper> mergedRegions = new TreeSet<CellRangeAddressWrapper>();
        for (int i = sheet.getFirstRowNum(); i <= sheet.getLastRowNum(); i++) {
            HSSFRow srcRow = sheet.getRow(i);
            HSSFRow destRow = newSheet.createRow(i);
            if (srcRow != null) {
                CopySheetUtil.copyRow(sheet, newSheet, srcRow, destRow,
                        styleMap, mergedRegions);
                if (srcRow.getLastCellNum() > maxColumnNum) {
                    maxColumnNum = srcRow.getLastCellNum();
                }
            }
        }
        for (int i = 0; i <= maxColumnNum; i++) {    //設置列寬
            newSheet.setColumnWidth(i, sheet.getColumnWidth(i));
        }
    }

    /**
     * 復制並合並單元格
     * @param srcSheet
     * @param destSheet
     * @param srcRow
     */
    public static void copyRow(HSSFSheet srcSheet, HSSFSheet destSheet,
                               HSSFRow srcRow, HSSFRow destRow,
                               Map<Integer, HSSFCellStyle> styleMap, Set<CellRangeAddressWrapper> mergedRegions) {
        try{
            //Set<CellRangeAddressWrapper> mergedRegions = new TreeSet<CellRangeAddressWrapper>();
            destRow.setHeight(srcRow.getHeight());
            int deltaRows = destRow.getRowNum() - srcRow.getRowNum(); //如果copy到另一個sheet的起始行數不同
            for (int j = srcRow.getFirstCellNum(); j <= srcRow.getLastCellNum(); j++) {
                HSSFCell oldCell = srcRow.getCell(j); // old cell
                HSSFCell newCell = destRow.getCell(j); // new cell
                if (oldCell != null) {
                    if (newCell == null) {
                        newCell = destRow.createCell(j);
                    }
                    copyCell(oldCell, newCell, styleMap);
                    CellRangeAddress mergedRegion = getMergedRegion(srcSheet,
                            srcRow.getRowNum(), (short) oldCell.getColumnIndex());
                    if (mergedRegion != null) {
                        CellRangeAddress newMergedRegion = new CellRangeAddress(
                                mergedRegion.getFirstRow() + deltaRows,
                                mergedRegion.getLastRow() + deltaRows, mergedRegion
                                .getFirstColumn(), mergedRegion
                                .getLastColumn());
                        CellRangeAddressWrapper wrapper = new CellRangeAddressWrapper(
                                newMergedRegion);
                        if (isNewMergedRegion(wrapper, mergedRegions)) {
                            boolean i = mergedRegions.add(wrapper);
                            if(i){
                                destSheet.addMergedRegion(wrapper.range);
                            }


                        }
                    }
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }

    }

    /**
     * 把原來的Sheet中cell(列)的樣式和數據類型復制到新的sheet的cell(列)中
     *
     * @param oldCell
     * @param newCell
     * @param styleMap
     */
    public static void copyCell(HSSFCell oldCell, HSSFCell newCell,
                                Map<Integer, HSSFCellStyle> styleMap) {
        if (styleMap != null) {
            if (oldCell.getSheet().getWorkbook() == newCell.getSheet()
                    .getWorkbook()) {
                newCell.setCellStyle(oldCell.getCellStyle());
            } else {
                int stHashCode = oldCell.getCellStyle().hashCode();
                HSSFCellStyle newCellStyle = styleMap.get(stHashCode);
                if (newCellStyle == null) {
                    newCellStyle = newCell.getSheet().getWorkbook()
                            .createCellStyle();
                    newCellStyle.cloneStyleFrom(oldCell.getCellStyle());
                    styleMap.put(stHashCode, newCellStyle);
                }
                newCell.setCellStyle(newCellStyle);
            }
        }
        switch (oldCell.getCellType().getCode()) {
            //case CellType.STRING:
            case 1:
                newCell.setCellValue(oldCell.getStringCellValue());
                break;
            //case CellType.NUMERIC:
            case 0:
                newCell.setCellValue(oldCell.getNumericCellValue());
                break;
            //case CellType.BLANK:
            case 3:
                newCell.setCellType(CellType.BLANK);
                break;
            //case CellType.BOOLEAN:
            case 4:
                newCell.setCellValue(oldCell.getBooleanCellValue());
                break;
            //case CellType.ERROR:
            case 5:
                newCell.setCellErrorValue(oldCell.getErrorCellValue());
                break;
            //case CellType.FORMULA:
            case 2:
                newCell.setCellFormula(oldCell.getCellFormula());
                break;
            default:
                break;
        }

    }

    // 獲取merge對象
    public static CellRangeAddress getMergedRegion(HSSFSheet sheet, int rowNum,
                                                   short cellNum) {
        for (int i = 0; i < sheet.getNumMergedRegions(); i++) {
            CellRangeAddress merged = sheet.getMergedRegion(i);
            if (merged.isInRange(rowNum, cellNum)) {
                return merged;
            }
        }
        return null;
    }

    private static boolean isNewMergedRegion(
            CellRangeAddressWrapper newMergedRegion,
            Set<CellRangeAddressWrapper> mergedRegions) {
        boolean bool = mergedRegions.contains(newMergedRegion);
        return !bool;
    }
}

其中為了判斷合並單元格是否一樣,自定義的類 

CellRangeAddressWrapper:
import org.apache.poi.ss.util.CellRangeAddress;

public class CellRangeAddressWrapper implements Comparable<CellRangeAddressWrapper>{
    public CellRangeAddress range;

    public CellRangeAddressWrapper(CellRangeAddress theRange) {
        this.range = theRange;
    }

    @Override
    public int compareTo(CellRangeAddressWrapper craw) {
        if (range.getFirstColumn() < craw.range.getFirstColumn()
                || range.getFirstRow() < craw.range.getFirstRow()) {
            return -1;
        } else if (range.getFirstColumn() == craw.range.getFirstColumn()
                && range.getFirstRow() == craw.range.getFirstRow()) {
            return 0;
        } else {
            return 1;
        }
    }
}

 

即可將源對象的sheet拷貝至目標對象中。
需要注意的是 生成 ExcelTools 類中方法 exportSheet 中有注釋掉的 生成文件加 序號 的方法。這個晚上可以找到很多。也貼一下,需要自定義 導出的方法,原生的會報錯。
/**
 * 重寫easypoi的工具類,原工具類是final類型的
 *
 */
public class ExcelExportUtil {

    public static       int    USE_SXSSF_LIMIT = 100000;
    public static final String SHEET_NAME      = "sheetName";

    private ExcelExportUtil() {
    }

    /**
     * 大數據量導出
     *
     * @param entity      表格標題屬性
     * @param pojoClass   Excel對象Class
     * @param server      查詢數據的接口
     * @param queryParams 查詢數據的參數
     */
    public static Workbook exportBigExcel(ExportParams entity, Class<?> pojoClass,
                                          IExcelExportServer server, Object queryParams) {
        ExcelBatchExportService batchServer = new ExcelBatchExportService();
        batchServer.init(entity, pojoClass);
        return batchServer.exportBigExcel(server, queryParams);
    }

    /**
     * 大數據量導出
     *
     * @param entity
     * @param excelParams
     * @param server      查詢數據的接口
     * @param queryParams 查詢數據的參數
     * @return
     */
    public static Workbook exportBigExcel(ExportParams entity, List<ExcelExportEntity> excelParams,
                                          IExcelExportServer server, Object queryParams) {
        ExcelBatchExportService batchServer = new ExcelBatchExportService();
        batchServer.init(entity, excelParams);
        return batchServer.exportBigExcel(server, queryParams);
    }


    /**
     * @param entity    表格標題屬性
     * @param pojoClass Excel對象Class
     * @param dataSet   Excel對象數據List
     */
    public static Workbook exportExcel(ExportParams entity, Class<?> pojoClass,
                                       Collection<?> dataSet) {
        Workbook workbook = getWorkbook(entity.getType(), dataSet.size());
        new SubExcelExportService().createSheet(workbook, entity, pojoClass, dataSet);
        return workbook;
    }

    private static Workbook getWorkbook(ExcelType type, int size) {
        if (ExcelType.HSSF.equals(type)) {
            return new HSSFWorkbook();
        } else if (size < USE_SXSSF_LIMIT) {
            return new XSSFWorkbook();
        } else {
            return new SXSSFWorkbook();
        }
    }

    /**
     * 根據Map創建對應的Excel
     *
     * @param entity     表格標題屬性
     * @param entityList Map對象列表
     * @param dataSet    Excel對象數據List
     */
    public static Workbook exportExcel(ExportParams entity, List<ExcelExportEntity> entityList,
                                       Collection<?> dataSet) {
        Workbook workbook = getWorkbook(entity.getType(), dataSet.size());
        ;
        new SubExcelExportService().createSheetForMap(workbook, entity, entityList, dataSet);
        return workbook;
    }

    /**
     * 根據Map創建對應的Excel(一個excel 創建多個sheet)
     *
     * @param list 多個Map key title 對應表格Title key entity 對應表格對應實體 key data
     *             Collection 數據
     * @return
     */
    public static Workbook exportExcel(List<Map<String, Object>> list, ExcelType type) {
        Workbook workbook = getWorkbook(type, 0);
        for (Map<String, Object> map : list) {
            //ExcelExportService service = new ExcelExportService();
            new SubExcelExportService().createSheet(workbook, (ExportParams) map.get("title"),
                    (Class<?>) map.get("entity"), (Collection<?>) map.get("data"));
        }
        return workbook;
    }

    /**
     * 導出文件通過模板解析,不推薦這個了,推薦全部通過模板來執行處理
     *
     * @param params    導出參數類
     * @param pojoClass 對應實體
     * @param dataSet   實體集合
     * @param map       模板集合
     * @return
     */
    @Deprecated
    public static Workbook exportExcel(TemplateExportParams params, Class<?> pojoClass,
                                       Collection<?> dataSet, Map<String, Object> map) {
        return new ExcelExportOfTemplateUtil().createExcelByTemplate(params, pojoClass, dataSet,
                map);
    }

    /**
     * 導出文件通過模板解析只有模板,沒有集合
     *
     * @param params 導出參數類
     * @param map    模板集合
     * @return
     */
    public static Workbook exportExcel(TemplateExportParams params, Map<String, Object> map) {
        return new ExcelExportOfTemplateUtil().createExcelByTemplate(params, null, null, map);
    }

    /**
     * 導出文件通過模板解析只有模板,沒有集合
     * 每個sheet對應一個map,導出到處,key是sheet的NUM
     *
     * @param params 導出參數類
     * @param map    模板集合
     * @return
     */
    public static Workbook exportExcel(Map<Integer, Map<String, Object>> map,
                                       TemplateExportParams params) {
        return new ExcelExportOfTemplateUtil().createExcelByTemplate(params, map);
    }

    /**
     * 導出文件通過模板解析只有模板,沒有集合
     * 每個sheet對應一個list,按照數量進行導出排序,key是sheet的NUM
     *
     * @param params 導出參數類
     * @param map    模板集合
     * @return
     */
    public static Workbook exportExcelClone(Map<Integer, List<Map<String, Object>>> map,
                                            TemplateExportParams params) {
        return new ExcelExportOfTemplateUtil().createExcelCloneByTemplate(params, map);
    }

}

其中的自定義  SubExcelExportService 類:

import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.afterturn.easypoi.excel.export.ExcelExportService;
import cn.afterturn.easypoi.excel.export.styler.IExcelExportStyler;
import cn.afterturn.easypoi.exception.excel.ExcelExportException;
import cn.afterturn.easypoi.exception.excel.enums.ExcelExportEnum;
import cn.afterturn.easypoi.util.PoiExcelGraphDataUtil;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;

import java.util.*;

public class SubExcelExportService extends ExcelExportService {
    private static int MAX_NUM = 60000;

    @Override
    protected void insertDataToSheet(Workbook workbook, ExportParams entity, List<ExcelExportEntity> entityList,
                                     Collection<?> dataSet, Sheet sheet) {
        try {
            dataHandler = entity.getDataHandler();
            if (dataHandler != null && dataHandler.getNeedHandlerFields() != null) {
                needHandlerList = Arrays.asList(dataHandler.getNeedHandlerFields());
            }
            dictHandler = entity.getDictHandler();
            i18nHandler = entity.getI18nHandler();
            // 創建表格樣式
            setExcelExportStyler(
                    (IExcelExportStyler) entity.getStyle().getConstructor(Workbook.class).newInstance(workbook));
            Drawing patriarch = PoiExcelGraphDataUtil.getDrawingPatriarch(sheet);
            List<ExcelExportEntity> excelParams = new ArrayList<ExcelExportEntity>();
            if (entity.isAddIndex()) {
                excelParams.add(indexExcelEntity(entity));
            }
            excelParams.addAll(entityList);
//            sortAllParams(excelParams);
            int index = entity.isCreateHeadRows() ? createHeaderAndTitle(entity, sheet, workbook, excelParams) : 0;
            int titleHeight = index;
            setCellWith(excelParams, sheet);
            setColumnHidden(excelParams, sheet);
            short rowHeight = entity.getHeight() != 0 ? entity.getHeight() : getRowHeight(excelParams);
            setCurrentIndex(1);
            Iterator<?> its = dataSet.iterator();
            List<Object> tempList = new ArrayList<Object>();
            while (its.hasNext()) {
                Object t = its.next();
                index += createCells(patriarch, index, t, excelParams, sheet, workbook, rowHeight, 0)[0];
                tempList.add(t);
                if (index >= MAX_NUM) {
                    break;
                }
            }
            if (entity.getFreezeCol() != 0) {
                sheet.createFreezePane(entity.getFreezeCol(), 0, entity.getFreezeCol(), 0);
            }

            mergeCells(sheet, excelParams, titleHeight);

            its = dataSet.iterator();
            for (int i = 0, le = tempList.size(); i < le; i++) {
                its.next();
                its.remove();
            }
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("List data more than max ,data size is {}", dataSet.size());
            }
            // 發現還有剩余list 繼續循環創建Sheet
            if (dataSet.size() > 0) {
                createSheetForMap(workbook, entity, entityList, dataSet);
            } else {
                // 創建合計信息
                addStatisticsRow(getExcelExportStyler().getStyles(true, null), sheet);
            }

        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            throw new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e.getCause());
        }
    }
}
需要注意的是,加入依賴的poi版本。3.幾的我忘了,會報錯,找不到貌似叫一個CellType的錯,忘了截圖了。需要更換至 4 以上的版本即可。

附帶序號的excel示例圖

 
        

 


免責聲明!

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



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