序言
前一篇文章《Freemarker導出複雜Excel圖文教程》,講解了使用Freemarker
導出複雜的Excel,本文將在前文的基礎之上,講解使用Freemarker
導出Excel後,在整合poi
插入圖片到Excel,從而實現Freemarker
導出帶有圖片的Excel工具。
爲什麼要做這個工具呢?
因爲Freemarker
是不支持導出帶有圖片的Excel的,不支持的原因是Freemarker
導出的Excel爲xml
格式,此格式所有的富文本信息都會丟失,只留下文本內容,所以不要在妄想用Freemarker
直接導出帶有圖片的Excel了,但是Freemarker
是可以導出帶有圖片的Word。基於此種需求,做此工具導出帶有圖片的Excel。
導出步驟
- 使用
Freemarker
將Excel中文本信息導出。參考前文:《Freemarker導出複雜Excel圖文教程》 - 將
xml
格式的Excel轉化爲xls
格式(最複雜步驟,絕非改個後綴名那麼簡單)。 - 通過
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工作,還是較爲繁瑣的,確實是個需要耐心慢慢做的工作。使用本文提供的工具,可以減輕不少的工作負擔,節省至少幾天的工期,最起碼以後在遇到類似工作,不會在擔心了。寫教程、寫示例項目也很費時,如果幫到了你,希望大家收藏、點贊。