Freemarker整合poi導出帶有圖片的Excel教程

序言

前一篇文章《Freemarker導出複雜Excel圖文教程》,講解了使用Freemarker導出複雜的Excel,本文將在前文的基礎之上,講解使用Freemarker導出Excel後,在整合poi插入圖片到Excel,從而實現Freemarker導出帶有圖片的Excel工具。

爲什麼要做這個工具呢?

因爲Freemarker是不支持導出帶有圖片的Excel的,不支持的原因是Freemarker導出的Excel爲xml格式,此格式所有的富文本信息都會丟失,只留下文本內容,所以不要在妄想用Freemarker直接導出帶有圖片的Excel了,但是Freemarker是可以導出帶有圖片的Word。基於此種需求,做此工具導出帶有圖片的Excel。

導出步驟

  1. 使用Freemarker將Excel中文本信息導出。參考前文:《Freemarker導出複雜Excel圖文教程》
  2. xml格式的Excel轉化爲xls格式(最複雜步驟,絕非改個後綴名那麼簡單)。
  3. 通過poi將圖片插入到Excel指定位置。

導出Excel效果圖

在這裏插入圖片描述
本文完整項目,提供下載

一.快速開始

從一個示例開始,演示下如何使用此工具。你只需要構建FreemakerInput對象,將做好的Freemarker模板放到template中,就可以調用工具類導出Excel了。用法是不是很簡單。示例:

@Component
public class ExportImageExcel {
   /**
     * 導出帶有圖片的Excel示例
     * 
     */
    public void export() {
        String imagePath = "";
        List<ExcelImageInput> excelImageInputs = new ArrayList<>();
        try {
            Enumeration<URL> urlEnumeration = this.getClass().getClassLoader().getResources("templates/image.png");
            URL url = urlEnumeration.nextElement();
            imagePath = url.getPath();
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 若改變圖片位置,修改後4個參數
        HSSFClientAnchor anchor = new HSSFClientAnchor(0, 0, 0, 0, (short)16, 1, (short)26, 27);
        ExcelImageInput excelImageInput = new ExcelImageInput(imagePath, 0, anchor);
        excelImageInputs.add(excelImageInput);
        FreemakerInput freemakerInput = new FreemakerInput();
        freemakerInput.setTemplateName("發票.ftl");
        freemakerInput.setTemplateFilePath("");
        freemakerInput.setDataMap(getExcelData());
        freemakerInput.setTemporaryXmlfile("export/temp/");
        freemakerInput.setExcelImageInputs(excelImageInputs);
        freemakerInput.setFileName("導出帶圖片Excel緩存文件");
        // 導出到項目所在目錄下,export文件夾中
        FreemarkerUtils.exportImageExcel("export/導出帶圖片Excel.xls", freemakerInput);
    }

    private Map<String, Object> getExcelData() {
          // 模擬Excel假數據,省略..
      }
}


二.導出Excel工具類講解

提供了兩種導出圖片的方式,一種導出到本地硬盤;另一種導出到HttpServletResponse中,即在接口中使用。

 /**
     * 導出到文件中(導出到硬盤)
     * 
     * @param excelFilePath
     * @param freemakerEntity
     * @author 大腦補丁 on 2020-04-14 15:34
     */
    public static void exportImageExcel(String excelFilePath, FreemakerInput freemakerEntity) {
        try {
            File file = new File(excelFilePath);
            FileUtils.forceMkdirParent(file);
            FileOutputStream outputStream = new FileOutputStream(file);
            createImageExcleToStream(freemakerEntity, outputStream);
            // 刪除xml緩存文件
            FileUtils
                .forceDelete(new File(freemakerEntity.getTemporaryXmlfile() + freemakerEntity.getFileName() + ".xml"));
            log.info("導出成功,導出到目錄:" + file.getCanonicalPath());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 導出到response輸出流中(用於瀏覽器調用接口)
     * 
     * @param excelFilePath
     * @param freemakerEntity
     * @author 大腦補丁 on 2020-04-14 15:34
     */
    public static void exportImageExcel(HttpServletResponse response, FreemakerInput freemakerEntity) {
        try {
            OutputStream outputStream = response.getOutputStream();
            // 寫入excel文件
            response.reset();
            response.setContentType("application/msexcel;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=\""
                + new String((freemakerEntity.getFileName() + ".xls").getBytes("GBK"), "ISO8859-1") + "\"");
            createImageExcleToStream(freemakerEntity, outputStream);
            // 刪除xml緩存文件
            FileUtils
                .forceDelete(new File(freemakerEntity.getTemporaryXmlfile() + freemakerEntity.getFileName() + ".xml"));
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

工具類爲FreemarkerUtils.java,完整的工具類代碼:


package com.study.commons.utils;

import java.awt.image.BufferedImage;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FileUtils;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
import org.apache.poi.hssf.usermodel.HSSFDataFormat;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.ClientAnchor;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.RegionUtil;
import org.dom4j.Document;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import com.study.dto.freemarker.input.ExcelImageInput;
import com.study.dto.freemarker.input.FreemakerInput;
import com.study.entity.excel.Cell;
import com.study.entity.excel.CellRangeAddressEntity;
import com.study.entity.excel.Column;
import com.study.entity.excel.Row;
import com.study.entity.excel.Style;
import com.study.entity.excel.Table;
import com.study.entity.excel.Worksheet;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;

/**
 * @project freemarker-excel
 * @description: freemarker工具類
 * @author 大腦補丁
 * @create 2020-04-14 09:43
 */
public class FreemarkerUtils {

    private static final Logger log = LoggerFactory.getLogger(FreemarkerUtils.class);

    /**
     * 導出Excel到指定文件
     * 
     * @param dataMap
     *            數據源
     * @param templateName
     *            模板名稱(包含文件後綴名.ftl)
     * @param templateFilePath
     *            模板所在路徑(不能爲空,當前路徑傳空字符:"")
     * @param fileFullPath
     *            文件完整路徑(如:usr/local/fileName.xls)
     * @author 大腦補丁 on 2020-04-05 11:51
     */
    @SuppressWarnings("rawtypes")
    public static void exportToFile(Map dataMap, String templateName, String templateFilePath, String fileFullPath) {
        try {
            File file = new File(fileFullPath);
            FileUtils.forceMkdirParent(file);
            FileOutputStream outputStream = new FileOutputStream(file);
            exportToStream(dataMap, templateName, templateFilePath, outputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 導出Excel到輸出流
     * 
     * @param dataMap
     *            數據源
     * @param templateName
     *            模板名稱(包含文件後綴名.ftl)
     * @param templateFilePath
     *            模板所在路徑(不能爲空,當前路徑傳空字符:"")
     * @param outputStream
     *            輸出流
     * @author 大腦補丁 on 2020-04-05 11:52
     */
    @SuppressWarnings("rawtypes")
    public static void exportToStream(Map dataMap, String templateName, String templateFilePath,
        FileOutputStream outputStream) {
        try {
            Template template = getTemplate(templateName, templateFilePath);
            OutputStreamWriter outputWriter = new OutputStreamWriter(outputStream, "UTF-8");
            Writer writer = new BufferedWriter(outputWriter);
            template.process(dataMap, writer);
            writer.flush();
            writer.close();
            outputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 導出到文件中(導出到硬盤)
     * 
     * @param excelFilePath
     * @param freemakerEntity
     * @author 大腦補丁 on 2020-04-14 15:34
     */
    public static void exportImageExcel(String excelFilePath, FreemakerInput freemakerEntity) {
        try {
            File file = new File(excelFilePath);
            FileUtils.forceMkdirParent(file);
            FileOutputStream outputStream = new FileOutputStream(file);
            createImageExcleToStream(freemakerEntity, outputStream);
            // 刪除xml緩存文件
            FileUtils
                .forceDelete(new File(freemakerEntity.getTemporaryXmlfile() + freemakerEntity.getFileName() + ".xml"));
            log.info("導出成功,導出到目錄:" + file.getCanonicalPath());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 導出到response輸出流中(用於瀏覽器調用接口)
     * 
     * @param excelFilePath
     * @param freemakerEntity
     * @author 大腦補丁 on 2020-04-14 15:34
     */
    public static void exportImageExcel(HttpServletResponse response, FreemakerInput freemakerEntity) {
        try {
            OutputStream outputStream = response.getOutputStream();
            // 寫入excel文件
            response.reset();
            response.setContentType("application/msexcel;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=\""
                + new String((freemakerEntity.getFileName() + ".xls").getBytes("GBK"), "ISO8859-1") + "\"");
            createImageExcleToStream(freemakerEntity, outputStream);
            // 刪除xml緩存文件
            FileUtils
                .forceDelete(new File(freemakerEntity.getTemporaryXmlfile() + freemakerEntity.getFileName() + ".xml"));
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    // 獲取項目templates文件夾下的模板
    private static Template getTemplate(String templateName, String filePath) throws IOException {
        Configuration configuration = new Configuration(Configuration.VERSION_2_3_28);
        configuration.setDefaultEncoding("UTF-8");
        configuration.setTemplateUpdateDelayMilliseconds(0);
        configuration.setEncoding(Locale.CHINA, "UTF-8");
        configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
        configuration.setClassForTemplateLoading(FreemarkerUtils.class, "/templates" + filePath);
        configuration.setOutputEncoding("UTF-8");
        return configuration.getTemplate(templateName, "UTF-8");
    }

    private static void createImageExcleToStream(FreemakerInput freemakerEntity, OutputStream outputStream) {
        Writer out = null;
        try {
            // 創建xml文件
            Template template = getTemplate(freemakerEntity.getTemplateName(), freemakerEntity.getTemplateFilePath());
            File tempXMLFile = new File(freemakerEntity.getTemporaryXmlfile() + freemakerEntity.getFileName() + ".xml");
            FileUtils.forceMkdirParent(tempXMLFile);
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(tempXMLFile), "UTF-8"));
            template.process(freemakerEntity.getDataMap(), out);
            if (log.isDebugEnabled()) {
                log.debug("1.完成將文本數據導入到XML文件中");
            }
            SAXReader reader = new SAXReader();
            Document document = reader.read(tempXMLFile);
            Map<String, Style> styleMap = readXmlStyle(document);
            log.debug("2.完成解析XML中樣式信息");
            List<Worksheet> worksheets = readXmlWorksheet(document);
            if (log.isDebugEnabled()) {
                log.debug("3.開始將XML信息寫入Excel,數據爲:" + worksheets.toString());
            }
            HSSFWorkbook wb = new HSSFWorkbook();
            for (Worksheet worksheet : worksheets) {
                HSSFSheet sheet = wb.createSheet(worksheet.getName());
                Table table = worksheet.getTable();
                List<Row> rows = table.getRows();
                List<Column> columns = table.getColumns();
                // 填充列寬
                int columnIndex = 0;
                for (int i = 0; i < columns.size(); i++) {
                    Column column = columns.get(i);
                    columnIndex = getCellWidthIndex(columnIndex, i, column.getIndex());
                    sheet.setColumnWidth(columnIndex, (int)column.getWidth() * 50);
                }
                int createRowIndex = 0;
                List<CellRangeAddressEntity> cellRangeAddresses = new ArrayList<>();
                for (int rowIndex = 0; rowIndex < rows.size(); rowIndex++) {
                    Row rowInfo = rows.get(rowIndex);
                    if (rowInfo == null) {
                        continue;
                    }
                    createRowIndex = getIndex(createRowIndex, rowIndex, rowInfo.getIndex());
                    HSSFRow row = sheet.createRow(createRowIndex);
                    if (rowInfo.getHeight() != null) {
                        Integer height = rowInfo.getHeight() * 20;
                        row.setHeight(height.shortValue());
                    }
                    List<Cell> cells = rowInfo.getCells();
                    if (CollectionUtils.isEmpty(cells)) {
                        continue;
                    }
                    int startIndex = 0;
                    for (int cellIndex = 0; cellIndex < cells.size(); cellIndex++) {
                        Cell cellInfo = cells.get(cellIndex);
                        if (cellInfo == null) {
                            continue;
                        }
                        // 獲取起始列
                        startIndex = getIndex(startIndex, cellIndex, cellInfo.getIndex());
                        HSSFCell cell = row.createCell(startIndex);
                        String styleID = cellInfo.getStyleID();
                        Style style = styleMap.get(styleID);
                        /*設置數據單元格格式*/
                        CellStyle dataStyle = wb.createCellStyle();
                        // 設置邊框樣式
                        setBorder(style, dataStyle);
                        // 設置對齊方式
                        setAlignment(style, dataStyle);
                        // 填充文本
                        setValue(wb, cellInfo, cell, style, dataStyle);
                        // 填充顏色
                        setCellColor(style, dataStyle);
                        cell.setCellStyle(dataStyle);
                        // 合併單元格
                        startIndex = getCellRanges(createRowIndex, cellRangeAddresses, startIndex, cellInfo, style);
                    }
                }
                // 添加合併單元格
                addCellRange(sheet, cellRangeAddresses);
            }
            // 加載圖片到excel
            log.debug("4.開始寫入圖片:" + freemakerEntity.getExcelImageInputs());
            if (!CollectionUtils.isEmpty(freemakerEntity.getExcelImageInputs())) {
                writeImageToExcel(freemakerEntity.getExcelImageInputs(), wb);
            }
            log.debug("5.完成寫入圖片:" + freemakerEntity.getExcelImageInputs());
            // 寫入excel文件,response字符流轉換成字節流,template需要字節流作爲輸出
            wb.write(outputStream);
            outputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("導出excel異常:" + e.getMessage());
        } finally {
            try {
                out.close();
            } catch (Exception e) {

            }
        }
    }

    public static Map<String, Style> readXmlStyle(Document document) {
        Map<String, Style> styleMap = XmlReader.getStyle(document);
        return styleMap;
    }

    public static List<Worksheet> readXmlWorksheet(Document document) {
        List<Worksheet> worksheets = XmlReader.getWorksheet(document);
        return worksheets;
    }

    private static int getIndex(int columnIndex, int i, Integer index) {
        if (index != null) {
            columnIndex = index - 1;
        }
        if (index == null && columnIndex != 0) {
            columnIndex = columnIndex + 1;
        }
        if (index == null && columnIndex == 0) {
            columnIndex = i;
        }
        return columnIndex;
    }

    private static int getCellWidthIndex(int columnIndex, int i, Integer index) {
        if (index != null) {
            columnIndex = index;
        }
        if (index == null && columnIndex != 0) {
            columnIndex = columnIndex + 1;
        }
        if (index == null && columnIndex == 0) {
            columnIndex = i;
        }
        return columnIndex;
    }

    /**
     * 設置邊框
     * 
     * @param style:
     * @param dataStyle:
     * @return void
     */
    private static void setBorder(Style style, CellStyle dataStyle) {
        if (style != null && style.getBorders() != null) {
            for (int k = 0; k < style.getBorders().size(); k++) {
                Style.Border border = style.getBorders().get(k);
                if (border != null) {
                    if ("Bottom".equals(border.getPosition())) {
                        dataStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex());
                        dataStyle.setBorderBottom(BorderStyle.THIN);
                    }
                    if ("Left".equals(border.getPosition())) {
                        dataStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex());
                        dataStyle.setBorderLeft(BorderStyle.THIN);
                    }
                    if ("Right".equals(border.getPosition())) {
                        dataStyle.setRightBorderColor(IndexedColors.BLACK.getIndex());
                        dataStyle.setBorderRight(BorderStyle.THIN);
                    }
                    if ("Top".equals(border.getPosition())) {
                        dataStyle.setTopBorderColor(IndexedColors.BLACK.getIndex());
                        dataStyle.setBorderTop(BorderStyle.THIN);
                    }
                }

            }
        }
    }

    @SuppressWarnings("rawtypes")
    private static void writeImageToExcel(List<ExcelImageInput> excelImageInputs, HSSFWorkbook wb)
        throws IOException {
        BufferedImage bufferImg = null;
        if (!CollectionUtils.isEmpty(excelImageInputs)) {
            for (ExcelImageInput excelImageInput : excelImageInputs) {
                Sheet sheet = wb.getSheetAt(excelImageInput.getSheetIndex());
                if (sheet == null) {
                    continue;
                }
                // 畫圖的頂級管理器,一個sheet只能獲取一個
                Drawing patriarch = sheet.createDrawingPatriarch();
                // anchor存儲圖片的屬性,包括在Excel中的位置、大小等信息
                HSSFClientAnchor anchor = excelImageInput.getAnchor();
                anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE);
                // 插入圖片
                String imagePath = excelImageInput.getImgPath();
                // 將圖片寫入到byteArray中
                ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
                bufferImg = ImageIO.read(new File(imagePath));
                // 圖片擴展名
                String imageType = imagePath.substring(imagePath.lastIndexOf(".") + 1, imagePath.length());
                ImageIO.write(bufferImg, imageType, byteArrayOut);
                // 通過poi將圖片寫入到Excel中
                patriarch.createPicture(anchor,
                    wb.addPicture(byteArrayOut.toByteArray(), HSSFWorkbook.PICTURE_TYPE_JPEG));
            }
        }
    }

    /**
     * 添加合併單元格
     * 
     * @param sheet:
     * @param cellRangeAddresses:
     * @return void
     */
    private static void addCellRange(HSSFSheet sheet, List<CellRangeAddressEntity> cellRangeAddresses) {
        if (!CollectionUtils.isEmpty(cellRangeAddresses)) {
            for (CellRangeAddressEntity cellRangeAddressEntity : cellRangeAddresses) {
                CellRangeAddress cellRangeAddress = cellRangeAddressEntity.getCellRangeAddress();
                sheet.addMergedRegion(cellRangeAddress);
                if (CollectionUtils.isEmpty(cellRangeAddressEntity.getBorders())) {
                    continue;
                }
                for (int k = 0; k < cellRangeAddressEntity.getBorders().size(); k++) {
                    Style.Border border = cellRangeAddressEntity.getBorders().get(k);
                    if (border == null) {
                        continue;
                    }
                    if ("Bottom".equals(border.getPosition())) {
                        RegionUtil.setBorderBottom(BorderStyle.THIN, cellRangeAddress, sheet);
                    }
                    if ("Left".equals(border.getPosition())) {
                        RegionUtil.setBorderLeft(BorderStyle.THIN, cellRangeAddress, sheet);
                    }
                    if ("Right".equals(border.getPosition())) {
                        RegionUtil.setBorderRight(BorderStyle.THIN, cellRangeAddress, sheet);
                    }
                    if ("Top".equals(border.getPosition())) {
                        RegionUtil.setBorderTop(BorderStyle.THIN, cellRangeAddress, sheet);
                    }
                }
            }
        }
    }

    /**
     * 設置對齊方式
     * 
     * @param style:
     * @param dataStyle:
     * @return void
     */
    private static void setAlignment(Style style, CellStyle dataStyle) {
        if (style != null && style.getAlignment() != null) {
            // 設置水平對齊方式
            String horizontal = style.getAlignment().getHorizontal();
            if (!ObjectUtils.isEmpty(horizontal)) {
                if ("Left".equals(horizontal)) {
                    dataStyle.setAlignment(HorizontalAlignment.LEFT);
                } else if ("Center".equals(horizontal)) {
                    dataStyle.setAlignment(HorizontalAlignment.CENTER);
                } else {
                    dataStyle.setAlignment(HorizontalAlignment.RIGHT);
                }
            }

            // 設置垂直對齊方式
            String vertical = style.getAlignment().getVertical();
            if (!ObjectUtils.isEmpty(vertical)) {
                if ("Top".equals(vertical)) {
                    dataStyle.setVerticalAlignment(VerticalAlignment.TOP);
                } else if ("Center".equals(vertical)) {
                    dataStyle.setVerticalAlignment(VerticalAlignment.CENTER);
                } else if ("Bottom".equals(vertical)) {
                    dataStyle.setVerticalAlignment(VerticalAlignment.BOTTOM);
                } else if ("JUSTIFY".equals(vertical)) {
                    dataStyle.setVerticalAlignment(VerticalAlignment.JUSTIFY);
                } else {
                    dataStyle.setVerticalAlignment(VerticalAlignment.DISTRIBUTED);
                }
            }
            // 設置換行
            String wrapText = style.getAlignment().getWrapText();
            if (!ObjectUtils.isEmpty(wrapText)) {
                dataStyle.setWrapText(true);
            }
        }
    }

    /**
     * 設置單元格背景填充色
     * 
     * @param style:
     * @param dataStyle:
     * @return void
     */
    private static void setCellColor(Style style, CellStyle dataStyle) {
        if (style != null && style.getInterior() != null) {
            if ("#FF0000".equals(style.getInterior().getColor())) {
                // 填充單元格
                dataStyle.setFillForegroundColor(IndexedColors.RED.getIndex());
                dataStyle.setFillBackgroundColor(IndexedColors.RED.getIndex());
            } else if ("#92D050".equals(style.getInterior().getColor())) {
                // 填充單元格
                dataStyle.setFillForegroundColor(IndexedColors.LIME.getIndex());
            }
            if ("Solid".equals(style.getInterior().getPattern())) {
                dataStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
            }
        }
    }

    /**
     * 構造合併單元格集合
     * 
     * @param createRowIndex:
     * @param cellRangeAddresses:
     * @param startIndex:
     * @param cellInfo:
     * @param style:
     * @return int
     */
    private static int getCellRanges(int createRowIndex, List<CellRangeAddressEntity> cellRangeAddresses,
        int startIndex, Cell cellInfo, Style style) {
        if (cellInfo.getMergeAcross() != null || cellInfo.getMergeDown() != null) {
            CellRangeAddress cellRangeAddress = null;
            if (cellInfo.getMergeAcross() != null && cellInfo.getMergeDown() != null) {
                int mergeAcross = startIndex;
                if (cellInfo.getMergeAcross() != 0) {
                    // 獲取該單元格結束列數
                    mergeAcross += cellInfo.getMergeAcross();
                }
                int mergeDown = createRowIndex;
                if (cellInfo.getMergeDown() != 0) {
                    // 獲取該單元格結束列數
                    mergeDown += cellInfo.getMergeDown();
                }
                cellRangeAddress =
                    new CellRangeAddress(createRowIndex, mergeDown, (short)startIndex, (short)mergeAcross);
            } else if (cellInfo.getMergeAcross() != null && cellInfo.getMergeDown() == null) {
                int mergeAcross = startIndex;
                if (cellInfo.getMergeAcross() != 0) {
                    // 獲取該單元格結束列數
                    mergeAcross += cellInfo.getMergeAcross();
                    // 合併單元格
                    cellRangeAddress =
                        new CellRangeAddress(createRowIndex, createRowIndex, (short)startIndex, (short)mergeAcross);
                }

            } else if (cellInfo.getMergeDown() != null && cellInfo.getMergeAcross() == null) {
                int mergeDown = createRowIndex;
                if (cellInfo.getMergeDown() != 0) {
                    // 獲取該單元格結束列數
                    mergeDown += cellInfo.getMergeDown();
                    // 合併單元格
                    cellRangeAddress =
                        new CellRangeAddress(createRowIndex, mergeDown, (short)startIndex, (short)startIndex);
                }
            }

            if (cellInfo.getMergeAcross() != null) {
                int length = cellInfo.getMergeAcross().intValue();
                for (int i = 0; i < length; i++) {
                    startIndex += cellInfo.getMergeAcross();
                }
            }
            CellRangeAddressEntity cellRangeAddressEntity = new CellRangeAddressEntity();
            cellRangeAddressEntity.setCellRangeAddress(cellRangeAddress);
            if (style != null && style.getBorders() != null) {
                cellRangeAddressEntity.setBorders(style.getBorders());
            }
            cellRangeAddresses.add(cellRangeAddressEntity);
        }
        return startIndex;
    }

    /**
     * 設置文本值內容
     * 
     * @param wb:
     * @param cellInfo:
     * @param cell:
     * @param style:
     * @param dataStyle:
     * @return void
     */
    private static void setValue(HSSFWorkbook wb, Cell cellInfo, HSSFCell cell, Style style, CellStyle dataStyle) {
        if (cellInfo.getData() != null) {
            HSSFFont font = wb.createFont();
            if (style != null && style.getFont() != null) {
                String color = style.getFont().getColor();
                if ("#FF0000".equals(color)) {
                    font.setColor(IndexedColors.RED.getIndex());
                } else if ("#000000".equals(color)) {
                    font.setColor(IndexedColors.BLACK.getIndex());
                }
            }
            if (!ObjectUtils.isEmpty(cellInfo.getData().getType()) && "Number".equals(cellInfo.getData().getType())) {
                cell.setCellType(CellType.NUMERIC);
            }
            if (style != null && style.getFont().getBold() > 0) {
                font.setBold(true);
            }
            if (style != null && !ObjectUtils.isEmpty(style.getFont().getFontName())) {
                font.setFontName(style.getFont().getFontName());
            }
            if (style != null && style.getFont().getSize() > 0) {
                // 設置字體大小道
                font.setFontHeightInPoints((short)style.getFont().getSize());
            }

            if (cellInfo.getData().getFont() != null) {
                if (cellInfo.getData().getFont().getBold() > 0) {
                    font.setBold(true);
                }
                if ("Number".equals(cellInfo.getData().getType())) {
                    cell.setCellValue(Float.parseFloat(cellInfo.getData().getFont().getText()));
                } else {
                    cell.setCellValue(cellInfo.getData().getFont().getText());
                }
                if (!ObjectUtils.isEmpty(cellInfo.getData().getFont().getCharSet())) {
                    font.setCharSet(Integer.valueOf(cellInfo.getData().getFont().getCharSet()));
                }
            } else {
                if ("Number".equals(cellInfo.getData().getType())) {
                    if (!ObjectUtils.isEmpty(cellInfo.getData().getText())) {
                        // cell.setCellValue(Float.parseFloat(cellInfo.getData().getText()));
                        cell.setCellValue(Float.parseFloat(cellInfo.getData().getText().replaceAll(",", "")));
                    }
                } else {
                    cell.setCellValue(cellInfo.getData().getText());

                }
            }

            if (style != null) {
                if (style.getNumberFormat() != null) {
                    String color = style.getFont().getColor();
                    if ("#FF0000".equals(color)) {
                        font.setColor(IndexedColors.RED.getIndex());
                    } else if ("#000000".equals(color)) {
                        font.setColor(IndexedColors.BLACK.getIndex());
                    }
                    if ("0%".equals(style.getNumberFormat().getFormat())) {
                        HSSFDataFormat format = wb.createDataFormat();
                        dataStyle.setDataFormat(format.getFormat(style.getNumberFormat().getFormat()));
                    } else {
                        dataStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("#,##0.00"));
                    }
                    // HSSFDataFormat format = wb.createDataFormat();
                    // dataStyle.setDataFormat(format.getFormat(style.getNumberFormat().getFormat()));
                }
            }
            dataStyle.setFont(font);
        }
    }
}


三.項目依賴

1.Maven依賴

以下這幾個依賴,爲本文工具類中,必須使用的,請根據自己項目Spring的版本,確定兼容版本。

<!-- 模板引擎 -->
<dependency>
	<groupId>org.freemarker</groupId>
	<artifactId>freemarker</artifactId>
</dependency>

<!-- java處理Excel文件 -->
<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>poi</artifactId>
	<version>4.1.0</version>
</dependency>


<!-- java解析XML文件 -->
<dependency>
	<groupId>dom4j</groupId>
	<artifactId>dom4j</artifactId>
</dependency>

<!-- 使用其中的FileUtils工具類 -->
<dependency>
	<groupId>commons-io</groupId>
	<artifactId>commons-io</artifactId>
	<version>2.6</version>
</dependency>

<!-- 使用註解,簡化代碼 -->
<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<scope>provided</scope>
 </dependency>

注意:如果項目中引入了spring-boot-starter-web這個依賴,將不需要再引入logback,lombok,Freemarker,dom4j這幾個依賴,因爲其中已經包含了,爲了演示,所以在pom.xml顯式的引入了這幾個依賴。

<!-- spring-boot-starter-web 已經包含了logback、lombok、Freemarker、dom4j依賴,這裏只顯式的引用下 -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>	

2.工具類中兩個入參對象

FreemakerInput.java入參主對象:

@Data
@SuppressWarnings("rawtypes")
public class FreemakerInput {
    /**
     * 加載數據
     */
    private Map dataMap;
    /**
     * 模版名稱
     */
    private String templateName;
    /**
     * 模版路徑
     */
    private String templateFilePath;
    /**
     * 生成文件名稱
     */
    private String fileName;

    /**
     * xml緩存文件路徑
     */
    private String temporaryXmlfile;

    /**
     * 插入圖片信息
     */
    List<ExcelImageInput> excelImageInputs;

}

ExcelImageInput .java插入Excel圖片信息對象:

@Data
@AllArgsConstructor
public class ExcelImageInput implements Serializable {

    /**
     * 圖片地址
     */
    private String imgPath;

    /**
     * sheet索引
     */
    private Integer sheetIndex;

    /**
     * 位置
     */
    private HSSFClientAnchor anchor;

}


3.工具類依賴的XML解析器

這個類是用來讀取Freemarker導出的xml格式的Excel。將xml格式的Excel轉化爲xls格式的以後,才能向Excel中插入圖片。

package com.study.commons.utils;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.Element;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import com.study.entity.excel.Cell;
import com.study.entity.excel.Column;
import com.study.entity.excel.Data;
import com.study.entity.excel.Font;
import com.study.entity.excel.Row;
import com.study.entity.excel.Style;
import com.study.entity.excel.Table;
import com.study.entity.excel.Worksheet;

/**
 * 
 * @project freemarker-excel
 * @description: 讀取XML文件工具
 * @author 大腦補丁
 * @create 2020-04-21 08:58
 */
public class XmlReader {

    // 獲取樣式
    @SuppressWarnings("rawtypes")
    public static Map<String, Style> getStyle(Document document) {
        // 創建一個LinkedHashMap用於存放style,按照id查找
        Map<String, Style> styleMap = new LinkedHashMap<String, Style>();
        // 新建一個Style類用於存放節點數據
        Style style = null;
        // 獲取根節點
        Element root = document.getRootElement();
        // 獲取根節點下的Styles節點
        Element styles = root.element("Styles");
        // 獲取Styles下的Style節點
        List styleList = styles.elements("Style");
        Iterator<?> it = styleList.iterator();
        while (it.hasNext()) {
            // 新建一個Style類用於存放節點數據
            style = new Style();
            Element e = (Element)it.next();
            String id = e.attributeValue("ID").toString();
            // 設置style的id
            style.setId(id);
            if (e.attributeValue("Name") != null) {
                String name = e.attributeValue("Name").toString();
                // 設置style的name
                style.setName(name);
            }
            // 獲取Style下的NumberFormat節點
            Element enumberFormat = e.element("NumberFormat");
            if (enumberFormat != null) {
                Style.NumberFormat numberFormat = new Style.NumberFormat();
                numberFormat.setFormat(enumberFormat.attributeValue("Format"));
                style.setNumberFormat(numberFormat);
            }
            Style.Alignment alignment = new Style.Alignment();
            // 獲取Style下的Alignment節點
            Element ealignment = e.element("Alignment");
            if (ealignment != null) {
                // 設置aligment的相關屬性,並且設置style的aliment屬性
                alignment.setHorizontal(ealignment.attributeValue("Horizontal"));
                alignment.setVertical(ealignment.attributeValue("Vertical"));
                alignment.setWrapText(ealignment.attributeValue("WrapText"));
                style.setAlignment(alignment);
            }
            // 獲取Style下的Borders節點
            Element Borders = e.element("Borders");
            if (Borders != null) {
                // 獲取Borders下的Border節點
                List Border = Borders.elements("Border");
                // 用迭代器遍歷Border節點
                Iterator<?> borderIterator = Border.iterator();
                List<Style.Border> lborders = new ArrayList<Style.Border>();
                while (borderIterator.hasNext()) {
                    Element bd = (Element)borderIterator.next();
                    Style.Border border = new Style.Border();
                    border.setPosition(bd.attributeValue("Position"));
                    if (bd.attribute("LineStyle") != null) {
                        border.setLinestyle(bd.attributeValue("LineStyle"));
                        int weight = Integer.parseInt(bd.attributeValue("Weight"));
                        border.setWeight(weight);
                        border.setColor(bd.attributeValue("Color"));
                    }
                    lborders.add(border);
                }
                style.setBorders(lborders);
            }

            // 設置font的相關屬性,並且設置style的font屬性
            Style.Font font = new Style.Font();
            Element efont = e.element("Font");
            font.setFontName(efont.attributeValue("FontName"));
            if (efont.attributeValue("Size") != null) {
                double size = Double.parseDouble(efont.attributeValue("Size"));
                font.setSize(size);
            }
            if (efont.attribute("Bold") != null) {
                int bold = Integer.parseInt(efont.attributeValue("Bold"));
                font.setBold(bold);
            }
            font.setColor(efont.attributeValue("Color"));
            style.setFont(font);
            // 設置Interior的相關屬性,並且設置style的interior屬性
            Style.Interior interior = new Style.Interior();
            if (e.element("Interior") != null) {
                Element einterior = e.element("Interior");
                interior.setColor(einterior.attributeValue("Color"));
                interior.setPattern(einterior.attributeValue("Pattern"));
            }
            style.setInterior(interior);
            if (e.element("Protection") != null) {
                Element protectione = e.element("Protection");
                Style.Protection protection = new Style.Protection();
                protection.setModifier(protectione.attributeValue("Protected"));
                style.setProtection(protection);
            }
            styleMap.put(id, style);

        }
        return styleMap;
    }

    @SuppressWarnings("unchecked")
    public static List<Worksheet> getWorksheet(Document document) {
        List<Worksheet> worksheets = new ArrayList<>();
        Element root = document.getRootElement();
        // 讀取根節點下的Worksheet節點
        List<Element> sheets = root.elements("Worksheet");
        if (CollectionUtils.isEmpty(sheets)) {
            return worksheets;
        }

        for (Element sheet : sheets) {
            Worksheet worksheet = new Worksheet();
            String name = sheet.attributeValue("Name");
            worksheet.setName(name);
            Table table = getTable(sheet);
            worksheet.setTable(table);
            worksheets.add(worksheet);
        }
        return worksheets;
    }

    private static Table getTable(Element sheet) {
        Element tableElement = sheet.element("Table");
        if (tableElement == null) {
            return null;
        }
        Table table = new Table();
        String expandedColumnCount = tableElement.attributeValue("ExpandedColumnCount");
        if (expandedColumnCount != null) {
            table.setExpandedColumnCount(Integer.parseInt(expandedColumnCount));
        }
        String expandedRowCount = tableElement.attributeValue("ExpandedRowCount");
        if (expandedRowCount != null) {
            table.setExpandedRowCount(Integer.parseInt(expandedRowCount));
        }
        String fullColumns = tableElement.attributeValue("FullColumns");
        if (fullColumns != null) {
            table.setFullColumns(Integer.parseInt(fullColumns));
        }

        String fullRows = tableElement.attributeValue("FullRows");
        if (fullRows != null) {
            table.setFullRows(Integer.parseInt(fullRows));
        }
        String defaultColumnWidth = tableElement.attributeValue("DefaultColumnWidth");
        if (defaultColumnWidth != null) {
            table.setDefaultColumnWidth(Double.valueOf(defaultColumnWidth).intValue());
        }

        String defaultRowHeight = tableElement.attributeValue("DefaultRowHeight");
        if (defaultRowHeight != null) {
            table.setDefaultRowHeight(Integer.parseInt(defaultRowHeight));
        }
        // 讀取列
        List<Column> columns = getColumns(tableElement, expandedColumnCount, defaultColumnWidth);
        table.setColumns(columns);

        // 讀取行
        List<Row> rows = getRows(tableElement);
        table.setRows(rows);
        return table;
    }

    @SuppressWarnings("unchecked")
    private static List<Row> getRows(Element tableElement) {
        List<Element> rowElements = tableElement.elements("Row");
        if (CollectionUtils.isEmpty(rowElements)) {
            return null;
        }
        List<Row> rows = new ArrayList<>();
        for (Element rowElement : rowElements) {
            Row row = new Row();
            String height = rowElement.attributeValue("Height");
            if (height != null) {
                row.setHeight(Double.valueOf(height).intValue());
            }

            String index = rowElement.attributeValue("Index");
            if (index != null) {
                row.setIndex(Integer.valueOf(index));
            }
            List<Cell> cells = getCells(rowElement);
            row.setCells(cells);
            rows.add(row);
        }
        return rows;
    }

    @SuppressWarnings("unchecked")
    private static List<Cell> getCells(Element rowElement) {
        List<Element> cellElements = rowElement.elements("Cell");
        if (CollectionUtils.isEmpty(cellElements)) {
            return null;
        }
        List<Cell> cells = new ArrayList<>();
        for (Element cellElement : cellElements) {
            Cell cell = new Cell();
            String styleID = cellElement.attributeValue("StyleID");
            if (styleID != null) {
                cell.setStyleID(styleID);
            }
            String mergeAcross = cellElement.attributeValue("MergeAcross");
            if (mergeAcross != null) {
                cell.setMergeAcross(Double.valueOf(mergeAcross).intValue());
            }

            String mergeDown = cellElement.attributeValue("MergeDown");
            if (mergeDown != null) {
                cell.setMergeDown(Double.valueOf(mergeDown).intValue());
            }

            String index = cellElement.attributeValue("Index");
            if (index != null) {
                cell.setIndex(Integer.valueOf(index));
            }
            Element dataElement = cellElement.element("Data");
            if (dataElement != null) {
                Data data = new Data();
                String type = dataElement.attributeValue("Type");
                String xmlns = dataElement.attributeValue("xmlns");
                data.setType(type);
                data.setXmlns(xmlns);
                data.setText(dataElement.getText());
                Element bElement = dataElement.element("B");
                Integer bold = null;
                Element fontElement = null;
                if (bElement != null) {
                    fontElement = bElement.element("Font");
                    bold = 1;
                }
                Element uElement = dataElement.element("U");
                if (uElement != null) {
                    fontElement = uElement.element("Font");
                }
                if (fontElement == null) {
                    fontElement = dataElement.element("Font");
                }
                if (fontElement != null) {
                    Font font = new Font();
                    String face = fontElement.attributeValue("Face");
                    if (face != null) {
                        font.setFace(face);
                    }
                    String charSet = fontElement.attributeValue("CharSet");
                    if (charSet != null) {
                        font.setCharSet(charSet);
                    }
                    String color = fontElement.attributeValue("Color");
                    if (color != null) {
                        font.setColor(color);
                    }
                    if (bold != null) {
                        font.setBold(bold);
                    }
                    font.setText(fontElement.getText());
                    data.setFont(font);
                }

                cell.setData(data);
            }
            cells.add(cell);
        }
        return cells;
    }

    @SuppressWarnings("unchecked")
    private static List<Column> getColumns(Element tableElement, String expandedRowCount, String defaultColumnWidth) {
        List<Element> columnElements = tableElement.elements("Column");
        if (CollectionUtils.isEmpty(columnElements)) {
            return null;
        }
        if (ObjectUtils.isEmpty(expandedRowCount)) {
            return null;
        }
        int defaultWidth = 60;
        if (!ObjectUtils.isEmpty(defaultColumnWidth)) {
            defaultWidth = Double.valueOf(defaultColumnWidth).intValue();
        }
        List<Column> columns = new ArrayList<>();
        int indexNum = 0;
        for (int i = 0; i < columnElements.size(); i++) {
            Column column = new Column();
            Element columnElement = columnElements.get(i);
            String index = columnElement.attributeValue("Index");
            if (index != null) {
                if (indexNum < Integer.valueOf(index) - 1) {
                    for (int j = indexNum; j < Integer.valueOf(index) - 1; j++) {
                        column = new Column();
                        column.setIndex(indexNum);
                        column.setWidth(defaultWidth);
                        columns.add(column);
                        indexNum += 1;
                    }
                }
                column = new Column();
            }
            column.setIndex(indexNum);
            String autoFitWidth = columnElement.attributeValue("AutoFitWidth");
            if (autoFitWidth != null) {
                column.setAutofitwidth(Double.valueOf(autoFitWidth).intValue());
            }
            String width = columnElement.attributeValue("Width");
            if (width != null) {
                column.setWidth(Double.valueOf(width).intValue());
            }
            columns.add(column);
            indexNum += 1;
        }
        if (columns.size() < Integer.valueOf(expandedRowCount)) {
            for (int i = columns.size() + 1; i <= Integer.valueOf(expandedRowCount); i++) {
                Column column = new Column();
                column.setIndex(i);
                column.setWidth(defaultWidth);
                columns.add(column);
            }
        }
        return columns;
    }
}


4.工具類和解析器依賴的自定義Excel對象

以下9個對象,是自定義解析Excel相關對象,用來將xml格式的Excel轉化爲xls格式。

①:表格對象

package com.study.entity.excel;

import java.util.List;

import lombok.Data;

@Data
public class Table {
    private Integer expandedColumnCount;

    private Integer expandedRowCount;

    private Integer fullColumns;

    private Integer fullRows;

    private Integer defaultColumnWidth;

    private Integer defaultRowHeight;

    private List<Column> columns;

    private List<Row> rows;

}


②Sheet頁對象

package com.study.entity.excel;

import lombok.Data;

@Data
public class Worksheet {

    private String Name;

    private Table table;

}


③列對象

package com.study.entity.excel;

import lombok.Data;

@Data
public class Column {

    private Integer index;
    private double width;
    private int autofitwidth;
}


④行對象

package com.study.entity.excel;

import java.util.List;

import lombok.Data;

@Data
public class Row {

    private Integer height;

    private List<Cell> cells;

    private Integer index;

}


⑤單元格對象

package com.study.entity.excel;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Cell {

    private String styleID;

    private Integer mergeAcross;

    private Integer MergeDown;

    private Data data;

    private Integer index;

}


⑥合併單元格信息對象:

package com.study.entity.excel;

import lombok.Data;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.List;

@Data
public class CellRangeAddressEntity {

    private CellRangeAddress cellRangeAddress;

    private List<Style.Border> borders;

}


⑦數據對象

package com.study.entity.excel;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Data {

    private String type;

    private String xmlns;

    private Font font;

    private String text;

}


⑧字體對象

package com.study.entity.excel;

import lombok.Data;

@Data
public class Font {

    private String face;

    private String charSet;

    private String color;

    private String text;

    private int bold;

}


⑨樣式對象

package com.study.entity.excel;

import java.util.List;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
public class Style {
    private String id;
    private String parent;
    private String name;
    private Alignment alignment;
    private List<Border> borders;
    private Font font;
    private Interior interior;
    private NumberFormat numberFormat;
    private Protection protection;

    public Style(String id, Alignment alignment, List<Border> borders, Font font, Interior interior) {
        this.id = id;
        this.alignment = alignment;
        this.borders = borders;
        this.font = font;
        this.interior = interior;
    }

    public Style(String id, NumberFormat numberFormat) {
        this.id = id;
        this.numberFormat = numberFormat;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Alignment {
        private String horizontal;
        private String vertical;
        private String wrapText;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Border {
        private String position;
        private String linestyle;
        private int weight;
        private String color;

    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Font {
        private String fontName;
        private double size;
        private int bold;
        private String color;
        private Integer CharSet;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Interior {
        private String color;
        private String pattern;

    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class NumberFormat {
        private String format;

    }

    // 權限修飾
    @Data
    @NoArgsConstructor
    public static class Protection {
        private String modifier;

    }

}


總結

至此,使用Freemarker導出複雜的Excel、導出帶有圖片的Excel教程已經完結,導出此類Excel工作,還是較爲繁瑣的,確實是個需要耐心慢慢做的工作。使用本文提供的工具,可以減輕不少的工作負擔,節省至少幾天的工期,最起碼以後在遇到類似工作,不會在擔心了。寫教程、寫示例項目也很費時,如果幫到了你,希望大家收藏、點贊。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章