基于easyexcel实现导出excel,包括导出图片以及导出下拉框
1.最基本的导出excel
1.引入maven
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.1.1</version>
</dependency>
2.写一个样式工具类
package com.electronic.web.controller.studyEasyExcelExport;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;
/**
* 导出excel样式罢了
*/
public class ExcelStyleUtils {
public static HorizontalCellStyleStrategy getStyle() {
// 头的策略
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
WriteFont headWriteFont = new WriteFont();
headWriteFont.setFontHeightInPoints((short) 15);
headWriteCellStyle.setWriteFont(headWriteFont);
// 内容的策略
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
WriteFont contentWriteFont = new WriteFont();
// 字体大小
contentWriteFont.setFontHeightInPoints((short) 13);
contentWriteCellStyle.setWriteFont(contentWriteFont);
// 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
//导出数据水平居中
contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
}
}
3.写一个导出的实体
package com.electronic.web.controller.studyEasyExcelExport;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.HeadFontStyle;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@HeadFontStyle(fontHeightInPoints = 10)
@HeadRowHeight(20)
public class WorkOrderExcelExportVO {
@ExcelProperty(value = "派单时间",order = 2)
@ColumnWidth(22)
private String orderTime;
@ExcelProperty(value = "标题",order = 1)
@ColumnWidth(25)
private String title;
@ExcelProperty(value = "工单编号",order = 0)
@ColumnWidth(25)
private String orderNo;
@ExcelProperty(value = "分类",order = 4)
@ColumnWidth(15)
private String eventTypeName;
@ExcelProperty(value = "事件内容",order = 6)
@ColumnWidth(25)
private String content;
@ExcelProperty(value = "事件地址",order = 7)
@ColumnWidth(25)
private String address;
@ExcelProperty(value = "类型",order = 5)
@ColumnWidth(15)
private String appealTypeName;
@ExcelProperty(value = "承办单位",order = 8)
@ColumnWidth(20)
private String deptName;
@ExcelProperty(value = "承办状态",order = 9)
@ColumnWidth(20)
private String mainState;
@ExcelProperty(value = "办理期限",order = 3)
@ColumnWidth(22)
private String handleTime;
}
4.写控制层
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.CollectionUtils;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
@RestController
@RequestMapping("/easyexcel/test")
@Slf4j
public class TestEasyExcelController {
@PostMapping("export/xlsx")
public ResponseEntity<InputStreamResource> exportXlsx() {
final Path tempWordPath = exportExcel();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDisposition(
ContentDisposition
.attachment()
.filename("***汇总表.xlsx", StandardCharsets.UTF_8)
.build());
try {
final InputStream in = Files.newInputStream(tempWordPath, StandardOpenOption.DELETE_ON_CLOSE);
return new ResponseEntity<>(new InputStreamResource(in, "***汇总表.xlsx"), headers, HttpStatus.OK);
} catch (IOException e) {
throw new IllegalStateException("文件导出失败..", e);
}
}
}
5.写业务层
也就是上面的exportExcel();这个方法
//下面可以理解为业务层的东西 应该是放在业务层去写的
public Path exportExcel() {
//根据条件查询指定数据 注释掉的知识业务罢了
// Set<Long> orderIdSet;
// if (null != commandPageParam.getMainDeptId()) if (!DEPT_OPT_SECTION.equals(commandPageParam.getMainDeptId()))
// orderIdSet = workOrderInfoRepository.selectOrderIdSetByDeptId(commandPageParam.getMainDeptId(), Boolean.TRUE);
// else
// orderIdSet = workOrderInfoRepository.selectOrderIdSetByDeptId(commandPageParam.getMainDeptId(), Boolean.FALSE);
// else
// orderIdSet = workOrderInfoRepository.selectOrderIdSetByDeptId(commandPageParam.getCurrentDeptId(), Boolean.FALSE);
// commandPageParam.setOrderIdSet(orderIdSet);
// final List<WorkOrderExcelExportVO> exportList = workOrderInfoRepository.selectExcelExportData(commandPageParam);
// 创建一个包含十行假数据的 List 模拟查出来数据
List<WorkOrderExcelExportVO> exportList = new ArrayList<>();
// 添加十行假数据到 exportList
for (int i = 1; i <= 10; i++) {
WorkOrderExcelExportVO workOrder = new WorkOrderExcelExportVO();
workOrder.setOrderTime("2023-07-19");
workOrder.setTitle("标题" + i);
workOrder.setOrderNo("工单编号" + i);
workOrder.setEventTypeName("分类" + i);
workOrder.setContent("事件内容" + i);
workOrder.setAddress("事件地址" + i);
workOrder.setAppealTypeName("类型" + i);
workOrder.setDeptName("承办单位" + i);
workOrder.setMainState("承办状态" + i);
workOrder.setHandleTime("办理期限" + i);
exportList.add(workOrder);
}
log.info("当前共有{}条数据", exportList.size());
final Path tempPath;
HorizontalCellStyleStrategy horizontalCellStyleStrategy = ExcelStyleUtils.getStyle();
try {
tempPath = Files.createTempFile("workorder", ".xlsx");
EasyExcel.write(tempPath.toFile(), WorkOrderExcelExportVO.class)
.registerWriteHandler(horizontalCellStyleStrategy)
.sheet("***派单汇总表66")
.doWrite(exportList);
} catch (IOException e) {
throw new IllegalStateException("文件导出失败, 可能为磁盘空间不足", e);
}
return tempPath;
}
6.自测通过(截图)
2. 在1基础上导出带图片的excel
1.实体的改动
@Getter
@Setter
@ToString(callSuper = true)
@ContentRowHeight(120)
public class RoadCheckExcelExportVO {
//查出来的图片url
@ExcelProperty(value="附件图片",converter = SxjgUrlImageConverter.class, order=11)
@ColumnWidth(30)
private String disposeUrl;
@ExcelProperty(value = "序号",order = 0)
@ColumnWidth(15)
private Integer index;
2.自定义的图片转换器 SxjgUrlImageConverter
package com.safesoft.domain.check.vo;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.util.IoUtils;
import org.springframework.util.ObjectUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileSystems;
import java.nio.file.Path;
/**
* [自定义 图片 转换器,根据图片的url将图片导入到表格中。]
*
* @author Kevin.Wan
* @date 2024/1/19
**/
public class SxjgUrlImageConverter implements Converter<String> {
@Override
public Class<?> supportJavaTypeKey() {
return String.class;
}
@Override
public WriteCellData<?> convertToExcelData(String value, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) throws IOException {
InputStream inputStream = null;
try {
if (ObjectUtils.isEmpty(value)) {
return new WriteCellData<>("图片链接为空");
}
// Assuming the value is a relative path
String absolutePath = getAbsolutePath(value);
File file = new File(absolutePath);
if (!file.exists()) {
return new WriteCellData<>("图片文件不存在");
}
inputStream = new FileInputStream(file);
byte[] bytes = IoUtils.toByteArray(inputStream);
return new WriteCellData<>(bytes);
} catch (Exception e) {
return new WriteCellData<>("图片获取异常");
} finally {
if (inputStream != null) {
inputStream.close();
}
}
}
/**
* 获取图片的真实路径
*
* @param relativePath 图片路径
* @return 真实路径
*/
private String getAbsolutePath(String relativePath) {
//因为我的图片 数据库存的地址 例如 store/af9227eaada9b744c950de869327adf7.jpg
//实际上我存放的位置是在 当前项目的根目录,上一级attachments的下面 ,就是为了能找到图片
final String fileBaseDir = "../attachments";
final Path storeDirPath = FileSystems.getDefault().getPath(fileBaseDir);
return storeDirPath.resolve(relativePath).toString();
}
}
3.效果图
3. 在1基础上导出下拉框,并且下拉框进行数据校验
3.1 实体的改动
package com.safesoft.domain.keysupervisionvehicle.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.safesoft.domain.keysupervisionvehicle.impl.DepartmentConverter;
import com.safesoft.domain.keysupervisionvehicle.impl.PlateColorConverter;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* [重点监测车辆导入模板实体]
*
* @author Kevin.Wan
* @date 2024/1/11
**/
@Getter
@Setter
@ToString
public class KeySupervisionVehicleExport {
/**
* 车牌号
*/
@ExcelProperty(value = "车牌号",order = 1)
@ColumnWidth(25)
private String plateNum;
/**
* 车牌颜色
* 车牌颜色只能是order = 2 如果这里修改,KeySupervisionVehicleSheetWriteHandler这个类的车牌颜色下拉框也要修改
*/
@ExcelProperty(value = "车牌颜色",converter = PlateColorConverter.class,order = 2)
@ColumnWidth(25)
private String plateColor;
/**
* 挂车车牌
*/
@ExcelProperty(value = "挂车车牌",order = 3)
@ColumnWidth(25)
private String trailerPlateNum;
/**
* 挂车车牌颜色
* 车牌颜色只能是order = 4 如果这里修改,KeySupervisionVehicleSheetWriteHandler这个类的车牌颜色下拉框也要修改
*/
@ExcelProperty(value = "挂车车牌颜色",converter = PlateColorConverter.class,order = 4)
@ColumnWidth(25)
private String trailerPlateColor;
3.2 车牌颜色的转换器PlateColorConverter
package com.safesoft.domain.keysupervisionvehicle.impl;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.converters.ReadConverterContext;
import com.alibaba.excel.converters.WriteConverterContext;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.safesoft.domain.keysupervisionvehicle.entity.PlateColorEnum;
/**
* [自定义 Excel 转换器,用于将车牌颜色的标识转换为对应的参数表plate-color的code。]
*
* @author Kevin.Wan
* @date 2024/1/17
**/
public class PlateColorConverter implements Converter<String> {
/**
* 返回支持的 Java 类型
*
* @return Java 类型
*/
@Override
public Class<String> supportJavaTypeKey() {
return String.class;
}
/**
* 返回支持的 Excel 类型
*
* @return Excel 类型
*/
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
/**
* 将 Excel 数据转换为 Java 数据 (将车牌颜色名称转换为车牌颜色code)
*
* @param context 转换上下文
* @return Java 数据
*/
@Override
public String convertToJavaData(ReadConverterContext<?> context) {
return PlateColorEnum.getPlateColorCode(context.getReadCellData().getStringValue());
}
/**
* 将 Java 数据转换为 Excel 数据 (将车牌颜色code转换为车牌颜色名称)
*
* @param context 转换上下文
* @return Excel 数据
*/
@Override
public WriteCellData<?> convertToExcelData(WriteConverterContext<String> context) {
return new WriteCellData<>(PlateColorEnum.getPlateColorName(context.getValue()));
}
}
3.3 业务层的改动
public Path downloadTemplate() {
LOGGER.info("导入模板下载");
final List<KeySupervisionVehicleExport> keySupervisionVehicleExportList = Collections.emptyList();
final Path tempPath;
HorizontalCellStyleStrategy horizontalCellStyleStrategy = ExcelStyleUtils.getStyle();
try {
tempPath = Files.createTempFile("keySupervisionVehicleExport", ".xlsx");
EasyExcelFactory.write(tempPath.toFile(), KeySupervisionVehicleExport.class)
.registerWriteHandler(horizontalCellStyleStrategy)
//添加头 *****************改动点******************
.head(KeySupervisionVehicleExport.class)
//派单中队下拉框数据校验处理器 *****************改动点******************
.registerWriteHandler(new KeySupervisionVehicleSheetWriteHandler())
.sheet("****导入模板表")
.doWrite(keySupervisionVehicleExportList);
} catch (IOException e) {
throw new IllegalStateException("文件导出失败, 可能为磁盘空间不足", e);
}
return tempPath;
}
3.4 下拉框数据校验处理器KeySupervisionVehicleSheetWriteHandler
package com.safesoft.domain.keysupervisionvehicle.impl;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import org.apache.poi.ss.usermodel.DataValidation;
import org.apache.poi.ss.usermodel.DataValidationConstraint;
import org.apache.poi.ss.usermodel.DataValidationHelper;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* [自定义的SheetWriteHandler实现类,用于在Excel中添加下拉框,并进行数据验证]
*
* @author Kevin.Wan
* @date 2024/1/12
**/
public class KeySupervisionVehicleSheetWriteHandler implements SheetWriteHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(KeySupervisionVehicleSheetWriteHandler.class);
/**
* 派单中队下拉框开始列的索引
*/
private static final Integer DEPARTMENT_SELECT_FIRST_COL = 9;
/**
* 派单中队下拉框结束列的索引
*/
private static final Integer DEPARTMENT_SELECT_LAST_COL = 9;
/**
* 重点监测车辆导入模板-派单中队下拉框(客户需求,只能是这四个大队)
*/
private final String[] departmentIdOptions = {"一中队", "二中队", "三中队", "四中队"};
/**
* 车牌颜色的下拉框选项列表(客户需求,这个对应sys_parameter表的plate-color类型)
*/
private final String[] plateColorOptions = {"黄牌", "蓝牌", "绿牌"};
/**
* 在创建Sheet后调用,用于设置下拉框和样式
*
* @param writeWorkbookHolder WriteWorkbookHolder
* @param writeSheetHolder WriteSheetHolder
*/
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
// 获取当前Sheet
final Sheet sheet = writeSheetHolder.getSheet();
// 获取数据验证的帮助类
final DataValidationHelper helper = sheet.getDataValidationHelper();
LOGGER.info("当前派单中队的列为选择项,添加下拉框,设置数据有效性");
// 设置下拉框范围,此例中是第10列(索引从0开始)的第1行到第10000行
final CellRangeAddressList optionCellRange = new CellRangeAddressList(0, 9999, DEPARTMENT_SELECT_FIRST_COL, DEPARTMENT_SELECT_LAST_COL);
// 创建数据验证约束,使用传入的选项列表
final DataValidationConstraint optionConstraint =
helper.createExplicitListConstraint(departmentIdOptions);
// 创建数据验证对象,将约束应用于指定的单元格范围
final DataValidation validation = helper.createValidation(optionConstraint, optionCellRange);
// 设置验证失败时的错误提示框
validation.createErrorBox("输入错误", "请选择下拉框之中内容");
// 设置是否显示错误提示框
validation.setShowErrorBox(true);
// 将数据验证添加到Sheet中
sheet.addValidationData(validation);
LOGGER.info("当前车牌颜色的列为选择项,添加下拉框,设置数据有效性");
for (int colIndex : new int[]{2, 4}) {
// 设置第2列的车牌颜色下拉框范围
final CellRangeAddressList plateColorOptionCellRange = new CellRangeAddressList(0, 9999, colIndex, colIndex);
// 创建数据验证约束,使用车牌颜色选项列表
final DataValidationConstraint plateColorOptionConstraint = helper.createExplicitListConstraint(plateColorOptions);
// 创建数据验证对象,将约束应用于指定的单元格范围
final DataValidation plateColorValidation = helper.createValidation(plateColorOptionConstraint, plateColorOptionCellRange);
// 设置验证失败时的错误提示框
plateColorValidation.createErrorBox("输入错误", "请选择正确的车牌颜色");
// 设置是否显示错误提示框
plateColorValidation.setShowErrorBox(true);
// 将数据验证添加到Sheet中
sheet.addValidationData(plateColorValidation);
}
}
}