效果
实现思路
前端图表用的是echarts显示,只需要让前端将图表以图片的形式,将图片的base64传过来,后端接收到这个base64,写入到工作簿中的sheet中即可。无论是折线图、饼图、柱状图都是一样的,以图片的形式将图片追加到sheet中,只需要设定好图表的偏移量即可。
实现
1、ExcelStyleUtil 导出样式工具类
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.afterturn.easypoi.excel.entity.params.ExcelForEachParams;
import cn.afterturn.easypoi.excel.export.styler.IExcelExportStyler;
import org.apache.poi.ss.usermodel.*;
public class ExcelStyleUtil implements IExcelExportStyler {
private static final short STRING_FORMAT = (short) BuiltinFormats.getBuiltinFormat("TEXT");
private static final short FONT_SIZE_TEN = 12;
private static final short FONT_SIZE_ELEVEN = 14;
private static final short FONT_SIZE_TWELVE = 18;
/**
* 大标题样式
*/
private CellStyle headerStyle;
/**
* 每列标题样式
*/
private CellStyle titleStyle;
/**
* 数据行样式
*/
private CellStyle styles;
public ExcelStyleUtil(Workbook workbook) {
this.init(workbook);
}
/**
* 初始化样式
* @param workbook
*/
private void init(Workbook workbook) {
this.headerStyle = initHeaderStyle(workbook);
this.titleStyle = initTitleStyle(workbook,true,FONT_SIZE_ELEVEN);
this.styles = initStyles(workbook);
}
@Override
public CellStyle getHeaderStyle(short i) {
return headerStyle;
}
@Override
public CellStyle getTitleStyle(short i) {
return titleStyle;
}
@Override
public CellStyle getTemplateStyles(boolean b, ExcelForEachParams excelForEachParams) {
return null;
}
@Override
public CellStyle getStyles(boolean b, ExcelExportEntity excelExportEntity) {
return styles;
}
@Override
public CellStyle getStyles(Cell cell, int i, ExcelExportEntity entity, Object o, Object o1) {
return getStyles(true, entity);
}
/**
* 设置隔行背景色
*/
public static CellStyle getStyles(Workbook workbook,boolean isBold,short size) {
CellStyle style = initTitleStyle(workbook,isBold,size);
style.setDataFormat(STRING_FORMAT);
return style;
}
/**
* 初始化--大标题样式
*/
private static CellStyle initHeaderStyle(Workbook workbook) {
CellStyle style = getBaseCellStyle(workbook);
style.setFont(getFont(workbook, FONT_SIZE_TWELVE, true));
return style;
}
/**
* 初始化--每列标题样式
*/
private static CellStyle initTitleStyle(Workbook workbook,boolean isBold,short size) {
CellStyle style = getBaseCellStyle(workbook);
style.setFont(getFont(workbook, size, isBold));
//背景色
style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
return style;
}
/**
* 初始化--数据行样式
*/
private static CellStyle initStyles(Workbook workbook) {
CellStyle style = getBaseCellStyle(workbook);
style.setFont(getFont(workbook, FONT_SIZE_TEN, false));
style.setDataFormat(STRING_FORMAT);
return style;
}
/**
* 基础样式
*/
private static CellStyle getBaseCellStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
//下边框
style.setBorderBottom(BorderStyle.THIN);
//左边框
style.setBorderLeft(BorderStyle.THIN);
//上边框
style.setBorderTop(BorderStyle.THIN);
//右边框
style.setBorderRight(BorderStyle.THIN);
//水平居中
style.setAlignment(HorizontalAlignment.CENTER);
//上下居中
style.setVerticalAlignment(VerticalAlignment.CENTER);
//设置自动换行
style.setWrapText(true);
return style;
}
/**
* 字体样式
* @param size 字体大小
* @param isBold 是否加粗
*/
private static Font getFont(Workbook workbook, short size, boolean isBold) {
Font font = workbook.createFont();
//字体样式
font.setFontName("宋体");
//是否加粗
font.setBold(isBold);
//字体大小
font.setFontHeightInPoints(size);
return font;
}
}
2、ExcelUtil 导出工具类
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import cn.afterturn.easypoi.excel.imports.ExcelImportService;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Decoder;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.*;
public class ExcelUtil {
/**
* excel 导出
* @param list 数据
* @param title 标题
* @param sheetName sheet名称
* @param pojoClass pojo类型
* @param fileName 文件名称
*/
public static void exportExcel(List<?> list, String title, String sheetName, Class<?> pojoClass, String fileName, HttpServletResponse response){
ExportParams exportParams = new ExportParams(title, sheetName, ExcelType.XSSF);
exportParams.setStyle(ExcelStyleUtil.class);
defaultExport(list, pojoClass, fileName, response, exportParams);
}
/**
* excel 导出
* @param list 数据
* @param pojoClass pojo类型
* @param fileName 文件名称
*/
public static void exportExcel(List<?> list, Class<?> pojoClass, String fileName, HttpServletResponse response){
ExportParams exportParams = new ExportParams();
exportParams.setStyle(ExcelStyleUtil.class);
defaultExport(list, pojoClass, fileName, response, exportParams);
}
/**
* list map 导出
* @param list 数据
* @param fileName 文件名称
*/
public static void exportExcel(List<Map<String, Object>> list, String fileName, HttpServletResponse response){
ExportParams exportParams = new ExportParams();
exportParams.setStyle(ExcelStyleUtil.class);
defaultExport(list, fileName, response);
}
/**
* 默认的 excel 导出
* @param list 数据
* @param pojoClass pojo类型
* @param fileName 文件名称
* @param exportParams 导出参数
*/
private static void defaultExport(List<?> list, Class<?> pojoClass, String fileName, HttpServletResponse response, ExportParams exportParams){
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list);
downLoadExcel(fileName, response, workbook);
}
/**
* 默认的 excel 导出
* @param list 数据
* @param fileName 文件名称
*/
private static void defaultExport(List<Map<String, Object>> list, String fileName, HttpServletResponse response){
Workbook workbook = ExcelExportUtil.exportExcel(list, ExcelType.XSSF);
downLoadExcel(fileName, response, workbook);
}
/**
* 下载
* @param fileName 文件名称
* @param response
* @param workbook excel数据
*/
public static void downLoadExcel(String fileName, HttpServletResponse response, Workbook workbook){
try {
response.setCharacterEncoding("UTF-8");
response.setHeader("content-Type", "application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName + "." + ExcelTypeEnum.XLS.getValue(), "UTF-8"));
workbook.write(response.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* excel 导入
* @param filePath excel文件路径
* @param titleRows 标题行
* @param headerRows 表头行
* @param pojoClass pojo类型
*/
public static <T> List<T> importExcel(String filePath, Integer titleRows, Integer headerRows, Class<T> pojoClass) throws IOException {
if (StringUtils.isBlank(filePath)) {
return null;
}
ImportParams params = new ImportParams();
params.setTitleRows(titleRows);
params.setHeadRows(headerRows);
params.setNeedSave(true);
params.setSaveUrl("/excel/");
try {
return ExcelImportUtil.importExcel(new File(filePath), pojoClass, params);
} catch (NoSuchElementException e) {
throw new IOException("模板不能为空");
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
/**
* excel 导入
* @param file 上传的文件
* @param titleRows 标题行
* @param headerRows 表头行
* @param needVerify 是否检验excel内容
* @param pojoClass pojo类型
*/
public static <T> List<T> importExcel(MultipartFile file, Integer titleRows, Integer headerRows, boolean needVerify, Class<T> pojoClass) throws IOException {
if (file == null) {
return null;
}
try {
return importExcel(file.getInputStream(), titleRows, headerRows, needVerify, pojoClass);
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
/**
* excel 导入
* @param inputStream 文件输入流
* @param titleRows 标题行
* @param headerRows 表头行
* @param needVerify 是否检验excel内容
* @param pojoClass pojo类型
*/
public static <T> List<T> importExcel(InputStream inputStream, Integer titleRows, Integer headerRows, boolean needVerify, Class<T> pojoClass) throws IOException {
if (inputStream == null) {
return null;
}
ImportParams params = new ImportParams();
params.setTitleRows(titleRows);
params.setHeadRows(headerRows);
params.setSaveUrl("upload/excel/");
params.setNeedSave(true);
params.setNeedVerify(needVerify);
try {
return ExcelImportUtil.importExcel(inputStream, pojoClass, params);
} catch (NoSuchElementException e) {
throw new IOException("excel文件不能为空");
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
/**
* Excel 类型枚举
*/
enum ExcelTypeEnum {
XLS("xls"), XLSX("xlsx");
private String value;
ExcelTypeEnum(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
/**
* 上传文件,返回一个workbook
* @param file
*/
public static Workbook importExcel(MultipartFile file) throws IOException {
File toFile = new File(file.getOriginalFilename());
Workbook workbook = null;
if(toFile.getPath().endsWith("xls")){
workbook = new HSSFWorkbook(file.getInputStream());
}else if(toFile.getPath().endsWith("xlsx")){
workbook = new XSSFWorkbook(file.getInputStream());
}else {
throw new RuntimeException("请确认你上传的文件类型");
}
return workbook;
}
/**
* 读取指定sheet的数据
* @param file 上传的文件
* @param sheetName 要读取的sheetName
* @param titleRows 表头行数
* @param headRows 标题行数
* @param startRows 表头之前有多少行不要的数据,从1开始,忽略空行
* @param readRows 要读取多少行数据,从0开始,比如读取十行,值就是9; 不指定时默认为0
* @param pojoClass 实体
*/
public static <T> List<T> importExcel(MultipartFile file,String sheetName,Integer titleRows,Integer headRows, Integer startRows,Integer readRows,Class<T> pojoClass) throws Exception {
Workbook workbook = importExcel(file);
int numberOfSheets = workbook.getNumberOfSheets();
List<T> list = null;
for (int i = 0; i < numberOfSheets; i++) {
String name = workbook.getSheetName(i).trim();
if (name.equals(sheetName) || name.endsWith(sheetName)){
ImportParams params = new ImportParams();
params.setTitleRows(titleRows);
params.setHeadRows(headRows);
params.setStartRows(startRows);
params.setReadRows(readRows);
//第几个sheet页
params.setStartSheetIndex(i);
final ExcelImportService excelImportService = new ExcelImportService();
ExcelImportResult<T> result = excelImportService.importExcelByIs(file.getInputStream(), pojoClass, params, false);
list = result.getList();
break;
}
}
return list;
}
/**
* 导出Excel,并在最后追加图片
* @param sheetName sheet名称
* @param wb Workbook对象
* @param imgUrl 图片的base64字符串
*/
public static Workbook getWorkbook(String sheetName,Workbook wb, String imgUrl) throws IOException {
if (wb == null) {
wb = new HSSFWorkbook();
}
// 在workbook中根据sheet名称获取指定的sheet
Sheet sheet = wb.getSheet(sheetName);
/*生成图表*/
if(!StringUtils.isEmpty(imgUrl)) {
//拆分base64编码后部分
String[] imgUrlArr = imgUrl.split("base64,");
byte[] buffer = new BASE64Decoder().decodeBuffer(imgUrlArr[1]);
//图片临时存放地址
String picPath = System.getProperty("user.dir")+"\\upload\\image\\pic.png";
//图片文件
File file = new File(picPath);
try {
//生成图片
OutputStream out = new FileOutputStream(file);//图片输出流
out.write(buffer);
out.flush();//清空流
out.close();//关闭流
// 将图片写入流中
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
BufferedImage bufferImg = ImageIO.read(new File(picPath));
ImageIO.write(bufferImg, "PNG", outStream);
// 利用Drawing将图片写入EXCEL
Drawing<?> drawing = sheet.createDrawingPatriarch();
//设置图表偏移量:第1个单元格中x轴的偏移量、第1个单元格中y轴的偏移量、 第2个单元格中x轴的偏移量、 第2个单元格中y轴的偏移量、第1个单元格的列号、第1个单元格的行号、 第2个单元格的列号、第2个单元格的行号
//HSSFClientAnchor anchor = new HSSFClientAnchor(0, 0, 0, 0, (short) 0, 6, (short) 9, 40);
ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 6, 9, 40);
drawing.createPicture(anchor, wb.addPicture(outStream.toByteArray(), HSSFWorkbook.PICTURE_TYPE_PNG));
} catch (Exception ex) {
ex.printStackTrace();
}
if (file.exists()) {
file.delete();//删除图片
}
}
return wb;
}
}
3、controller
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import com.common.util.ExcelStyleUtil;
import com.common.util.ExcelUtil;
import lombok.Data;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/test")
public class TestController {
@Data
public static class Test{
@Excel(name = "名称",width = 25)
private String name;
@Excel(name = "1月",width = 15)
private Double m1;
@Excel(name = "2月",width = 15)
private Double m2;
@Excel(name = "3月",width = 15)
private Double m3;
@Excel(name = "4月",width = 15)
private Double m4;
@Excel(name = "5月",width = 15)
private Double m5;
@Excel(name = "6月",width = 15)
private Double m6;
@Excel(name = "7月",width = 15)
private Double m7;
@Excel(name = "8月",width = 15)
private Double m8;
@Excel(name = "9月",width = 15)
private Double m9;
@Excel(name = "10月",width = 15)
private Double m10;
@Excel(name = "11月",width = 15)
private Double m11;
@Excel(name = "12月",width = 15)
private Double m12;
public Test(String name, Double m1, Double m2, Double m3, Double m4, Double m5, Double m6, Double m7, Double m8, Double m9, Double m10, Double m11, Double m12) {
this.name = name;
this.m1 = m1;
this.m2 = m2;
this.m3 = m3;
this.m4 = m4;
this.m5 = m5;
this.m6 = m6;
this.m7 = m7;
this.m8 = m8;
this.m9 = m9;
this.m10 = m10;
this.m11 = m11;
this.m12 = m12;
}
}
@PostMapping("/export")
public void export(String imgUrl, HttpServletResponse response) throws IOException {
List<Test> list = new ArrayList<>();
list.add(new Test("工业",885.0,845.0,832.0,878.0,996.0,763.0,820.0,848.0,798.0,925.0,933.0,777.96));
list.add(new Test("农业",224.32,177.0,141.75,231.23,254.22,300.25,244.32,177.88,123.0,280.88,235.74,333.0));
list.add(new Test("商业",650.36,622.25,672.47,599.0,548.0,621.36,647.14,420.25,752.98,600.75,567.1,643.0));
list.add(new Test("其他",444.3,486.0,421.24,431.22,333.1,420.28,398.74,385.57,324.0,420.75,421.12,468.0));
ExportParams params = new ExportParams();
params.setTitle("年电量统计");
params.setTitleHeight((short) 15);
params.setHeight((short) 12);
params.setStyle(ExcelStyleUtil.class);
params.setSheetName("电量统计");
Workbook workbook = ExcelExportUtil.exportExcel(params,Test.class, list);
workbook = ExcelUtil.getWorkbook("电量统计", workbook, imgUrl);
ExcelUtil.downLoadExcel("年电量统计",response,workbook);
}
}