maven依赖
<!-- easy excel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.0.5</version>
</dependency>
获取下拉数据接口类, 下拉数据需实现此接口类
public interface ExcelSelectorService {
/**
* 获取下拉数据
*
* @return java.lang.String[]
* @author SunLingDa
* @date 2022/9/20 16:50
*/
String[] getSelectorData();
/**
* 根据字典key获取下拉数据
*
* @param dictKeyValue 字典key
* @return java.lang.String[]
* @author SunLingDa
* @date 2022/9/20 16:50
*/
String[] getSelectorData(String dictKeyValue);
}
下拉选的注解类, 可以固定数据, 也可以根据字典获取数据, 根据自己的业务处理
public @interface ExcelSelector {
/**
* 固定数据
*/
String[] fixedSelector() default {};
/**
* 字典key
*/
String dictKeyValue() default "";
/**
* 服务类
*/
Class<? extends ExcelSelectorService>[] serviceClass() default {};
}
下拉选处理类, 方法resolveExcelSelector获取下拉数据
@Data
@Slf4j
public class ExcelSelectorResolve {
/**
* 下拉选起始行
*/
private int startRow = 0;
/**
* 下拉选结束行
*/
private int endRow = 500;
/**
* 下拉数据集
*/
private String[] selectorData;
/**
* 解决Excel注解的下拉选数据获取
*
* @param excelSelector Excel下拉选
* @return java.lang.String[]
* @author SunLingDa
* @date 2022/9/20 16:58
*/
public String[] resolveExcelSelector(ExcelSelector excelSelector) {
if (excelSelector == null) {
return null;
}
String[] fixedSelector = excelSelector.fixedSelector();
if (ArrayUtil.isNotEmpty(fixedSelector)) {
return fixedSelector;
}
String[] selectorData = null;
Class<? extends ExcelSelectorService>[] serviceClass = excelSelector.serviceClass();
if (ArrayUtil.isNotEmpty(serviceClass)) {
try {
ExcelSelectorService excelSelectorService = serviceClass[0].newInstance();
if (StrUtil.isBlank(excelSelector.dictKeyValue())) {
selectorData = excelSelectorService.getSelectorData();
} else {
selectorData = excelSelectorService.getSelectorData(excelSelector.dictKeyValue());
}
} catch (InstantiationException | IllegalAccessException e) {
log.error(e.getMessage(), e);
}
}
return selectorData;
}
}
数据写入的处理器类
@Data
public class ExcelSelectorDataWriteHandler implements SheetWriteHandler {
private final Map<Integer, ExcelSelectorResolve> selectedMap;
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
Sheet sheet = writeSheetHolder.getSheet();
DataValidationHelper helper = sheet.getDataValidationHelper();
if (CollUtil.isEmpty(selectedMap)) {
return;
}
selectedMap.forEach((k, v) -> {
// 下拉 首行 末行 首列 末列
CellRangeAddressList list = new CellRangeAddressList(v.getStartRow(), v.getEndRow(), k, k);
// 下拉值
DataValidationConstraint constraint = helper.createExplicitListConstraint(v.getSelectorData());
DataValidation validation = helper.createValidation(constraint, list);
validation.setErrorStyle(DataValidation.ErrorStyle.STOP);
validation.setShowErrorBox(true);
validation.setSuppressDropDownArrow(true);
validation.createErrorBox("提示", "请输入下拉选项中的内容");
sheet.addValidationData(validation);
});
}
}
Excel数据监听类
@Data
@Slf4j
public class ExcelCellDataListener<T> implements ReadListener<T> {
/**
* 数据集合
*/
private List<T> data = CollUtil.newArrayList();
private Map<Integer, ExcelFailRecord> failMap = CollUtil.newHashMap(16);
@Override
public void invoke(T bean, AnalysisContext context) {
boolean emptyRow = true;
List<Field> fieldList = getFieldList(bean.getClass());
for (Field field : fieldList) {
field.setAccessible(true);
try {
Object fieldValue = field.get(bean);
if (fieldValue instanceof String) {
if (StrUtil.isNotBlank((String) fieldValue)) {
emptyRow = false;
break;
}
}
if (ObjectUtil.isNotNull(fieldValue)) {
emptyRow = false;
break;
}
} catch (IllegalAccessException e) {
log.error(e.getMessage(), e);
}
}
if (!emptyRow) {
// 不处理空数据行
data.add(bean);
ReadRowHolder readRowHolder = context.readRowHolder();
log.info("rowIndex: {}, rowType: {}", readRowHolder.getRowIndex(), readRowHolder.getRowType());
}
}
@Override
public void onException(Exception exception, AnalysisContext context) {
log.error(exception.getMessage(), exception);
if (exception instanceof ExcelDataConvertException) {
ExcelDataConvertException e = (ExcelDataConvertException) exception;
ExcelFailRecord excelFailRecord = new ExcelFailRecord();
excelFailRecord.setRow(e.getRowIndex());
excelFailRecord.setColumn(e.getColumnIndex());
excelFailRecord.setFailMessage(e.getCause().getMessage());
failMap.put(e.getRowIndex(), excelFailRecord);
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
log.info("Excel Deal Finish");
}
}
导出Excel工具类
@Slf4j
public class ExcelUtil {
/**
* 默认的sheet名称
*/
private static final String DEFAULT_SHEET_NAME = "Sheet1";
/**
* 写Excel数据
*
* @param response response
* @param fileName 文件名称
* @param data 数据
* @param clazz 类class
* @author SunLingDa
* @date 2022/9/6 13:59
*/
public static <T> void writeExcel(HttpServletResponse response, String fileName, List<T> data, Class<?> clazz) {
writeExcel(response, fileName, DEFAULT_SHEET_NAME, data, clazz);
}
/**
* 写Excel数据
*
* @param response response
* @param fileName 文件名称
* @param sheetName sheet名称
* @param data 数据
* @param clazz 类class
* @author SunLingDa
* @date 2022/9/6 13:58
*/
public static <T> void writeExcel(HttpServletResponse response, String fileName, String sheetName, List<T> data, Class<?> clazz) {
OutputStream outputStream = null;
Map<Integer, ExcelSelectorResolve> selectedMap = getSelectedMap(clazz);
ExcelSelectorDataWriteHandler writeHandler = new ExcelSelectorDataWriteHandler(selectedMap);
try {
outputStream = getOutputStream(response, fileName, ExcelTypeEnum.XLSX);
EasyExcel.write(outputStream, clazz).excelType(ExcelTypeEnum.XLSX).sheet(sheetName).registerWriteHandler(writeHandler).doWrite(data);
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
}
}
/**
* 读取不包含头信息的Excel
*
* @param file 文件
* @param clazz 类class
* @author SunLingDa
* @date 2022/9/6 13:20
*/
public static <T> List<T> readExcelNotContainHeader(MultipartFile file, Class<T> clazz) throws IOException {
return readExcel(1, file, clazz);
}
/**
* 读取包含头信息的Excel
*
* @param file 文件
* @param clazz 类class
* @author SunLingDa
* @date 2022/9/6 13:20
*/
public static <T> List<T> readExcelContainHeader(MultipartFile file, Class<T> clazz) throws IOException {
return readExcel(0, file, clazz);
}
/**
* 读取Excel
*
* @param rowNum 行数
* @param file 文件
* @param clazz 类class
* @author SunLingDa
* @date 2022/9/6 13:20
*/
public static <T> List<T> readExcel(int rowNum, MultipartFile file, Class<T> clazz) throws IOException {
String fileName = file.getOriginalFilename();
InputStream inputStream = file.getInputStream();
return readExcel(rowNum, fileName, inputStream, clazz);
}
/**
* 读取不包含头信息的Excel
*
* @param fileName 文件名称
* @param inputStream 流
* @param clazz 类
* @author SunLingDa
* @date 2022/9/6 13:16
*/
public static <T> List<T> readExcelNotContainHeader(String fileName, InputStream inputStream, Class<T> clazz) {
return readExcel(1, fileName, inputStream, clazz);
}
/**
* 读取包含头信息的Excel
*
* @param fileName 文件名称
* @param inputStream 流
* @param clazz 类
* @param listener 监听
* @author SunLingDa
* @date 2022/9/6 13:16
*/
public static <T> List<T> readExcelContainHeader(String fileName, InputStream inputStream, Class<T> clazz, ExcelCellDataListener<T> listener) {
return readExcel(0, fileName, inputStream, clazz);
}
/**
* 读取Excel
*
* @param rowNum 行数
* @param fileName 文件名称
* @param inputStream 流
* @param clazz 类
* @author SunLingDa
* @date 2022/9/6 13:16
*/
public static <T> List<T> readExcel(int rowNum, String fileName, InputStream inputStream, Class<T> clazz) {
ExcelCellDataListener<T> dataListener = new ExcelCellDataListener<>();
try {
ExcelReader excelReader = getExcelReader(rowNum, fileName, inputStream, clazz, dataListener);
if (excelReader == null) {
return null;
}
List<ReadSheet> sheetList = excelReader.excelExecutor().sheetList();
for (ReadSheet sheet : sheetList) {
excelReader.read(sheet);
}
excelReader.finish();
} finally {
try {
inputStream.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
return dataListener.getData();
}
/**
* 获取OutputStream
*
* @param response response
* @param fileName 文件名称
* @return java.io.OutputStream
* @author SunLingDa
* @date 2022/9/6 13:44
*/
private static OutputStream getOutputStream(HttpServletResponse response, String fileName, ExcelTypeEnum typeEnum) throws Exception {
fileName = URLEncoder.encode(fileName, "UTF-8");
response.setStatus(200);
response.setCharacterEncoding("UTF-8");
if (ExcelTypeEnum.CSV.equals(typeEnum)) {
response.setContentType("application/csv");
} else {
response.setContentType("application/vnd.ms-excel");
}
response.setHeader("Content-Disposition", "attachment;filename=" + fileName + typeEnum.getValue());
return response.getOutputStream();
}
/**
* 获取ExcelReader
*
* @param rowNum 行数
* @param fileName 文件名称
* @param inputStream 流
* @param clazz 类class
* @param listener 监听
* @return com.alibaba.excel.ExcelReader
* @author SunLingDa
* @date 2022/9/6 13:19
*/
private static ExcelReader getExcelReader(int rowNum, String fileName, InputStream inputStream, Class<?> clazz, ReadListener listener) {
if (StrUtil.isBlank(fileName)) {
return null;
}
String fileExtName = getFileExtName(fileName);
EasyExcelTypeEnum typeEnum = EasyExcelTypeEnum.parseType(fileExtName);
if (typeEnum == null) {
log.info("表格类型错误");
}
return EasyExcel.read(inputStream, clazz, listener).headRowNumber(rowNum).build();
}
/**
* 获取文件后缀名称 .xxx
*
* @param fileName 文件名称
* @return java.lang.String
* @author SunLingDa
* @date 2022/9/6 11:23
*/
private static String getFileExtName(String fileName) {
if (StrUtil.isBlank(fileName)) {
return null;
}
int lastIndex = fileName.lastIndexOf(StrUtil.DOT);
if (lastIndex != -1) {
return fileName.substring(lastIndex);
}
return null;
}
/**
* 获取样式
*
* @return com.alibaba.excel.write.style.HorizontalCellStyleStrategy
* @author SunLingDa
* @date 2022/9/6 16:20
*/
private static HorizontalCellStyleStrategy getStyleStrategy() {
// 表头样式
WriteCellStyle headStyle = new WriteCellStyle();
// 设置表头居中对齐
headStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
// 内容样式
WriteCellStyle contentStyle = new WriteCellStyle();
// 设置内容靠左对齐
contentStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);
return new HorizontalCellStyleStrategy(headStyle, contentStyle);
}
/**
* 获取下拉的map
*
* @param clazz 类class
* @return java.util.Map
* @author SunLingDa
* @date 2022/9/20 17:45
*/
private static Map<Integer, ExcelSelectorResolve> getSelectedMap(Class<?> clazz) {
Map<Integer, ExcelSelectorResolve> selectedMap = CollUtil.newHashMap();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (!field.isAnnotationPresent(ExcelSelector.class) || !field.isAnnotationPresent(ExcelProperty.class)) {
continue;
}
ExcelSelector excelSelector = field.getAnnotation(ExcelSelector.class);
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
ExcelSelectorResolve resolve = new ExcelSelectorResolve();
String[] data = resolve.resolveExcelSelector(excelSelector);
if (ArrayUtil.isNotEmpty(data)) {
resolve.setSelectorData(data);
selectedMap.put(excelProperty.index(), resolve);
}
}
return selectedMap;
}
}
下拉获取数据实现类, 我的是获取字典数据, 根据自己业务处理
@Slf4j
@Service
public class DeviceExcelSelectorServiceImpl implements ExcelSelectorService {
@Override
public String[] getSelectorData() {
return null;
}
@Override
public String[] getSelectorData(String dictKeyValue) {
IDataDictService dataDictService = SpringContextUtil.getBean(IDataDictService.class);
List<DataDict> list = dataDictService.getDataByKey(dictKeyValue);
if (CollUtil.isEmpty(list)) {
return null;
}
return list.stream().map(DataDict::getLabel).toArray(String[]::new);
}
}
获取上下文工具类
@Component
@Lazy(value = false)
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
if (SpringContextUtil.applicationContext == null) {
SpringContextUtil.applicationContext = applicationContext;
}
}
/**
* 获取ApplicationContext
*
* @return org.springframework.context.ApplicationContext
* @author SunLingDa
* @date 2022/8/22 17:05
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 根据名称获取Bean对象
*
* @param name 名称
* @return T
* @author SunLingDa
* @date 2022/8/22 17:05
*/
public static <T> T getBean(String name) {
//noinspection unchecked
return (T) applicationContext.getBean(name);
}
/**
* 根据class获取Bean对象
*
* @param clazz class
* @return T
* @author SunLingDa
* @date 2022/8/22 17:06
*/
public static <T> T getBean(Class<T> clazz) {
return applicationContext.getBean(clazz);
}
/**
* 根据名称和class获取Bean对象
*
* @param name 名称
* @param clazz class
* @return T
* @author SunLingDa
* @date 2022/8/22 17:06
*/
public static <T> T getBean(String name, Class<T> clazz) {
return applicationContext.getBean(name, clazz);
}
/**
* 获取配置文件数据
*
* @param key key
* @return java.lang.String
* @author SunLingDa
* @date 2022/8/23 11:38
*/
public static String getProperty(String key) {
return applicationContext.getEnvironment().getProperty(key);
}
/**
* 注册bean
*
* @param beanName bean名称
* @param bean bean
* @author SunLingDa
* @date 2022/8/23 11:39
*/
public static <T> void registerBean(String beanName, T bean) {
ConfigurableApplicationContext context = (ConfigurableApplicationContext) applicationContext;
context.getBeanFactory().registerSingleton(beanName, bean);
}
}
导出对应类, 固定值和获取字典值, DeviceExcelSelectorServiceImpl实现ExcelSelectorService接口
@Data
@ColumnWidth(20)
@HeadStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER)
@ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.LEFT)
public class CameraExportExcel {
@ExcelProperty(value = "设备名称", index = 0)
private String name;
@ExcelProperty(value = "设备编号", index = 1)
private String deviceId;
@ExcelIgnore
private String commTypeCode;
@ExcelProperty(value = "视频接入协议", index = 2)
@ExcelSelector(serviceClass = DeviceExcelSelectorServiceImpl.class, dictKeyValue = "videoAccessProtocol")
private String commTypeName;
@ExcelSelector(fixedSelector = {"是", "否"})
@ExcelProperty(value = "是否配有IM采集模块", index = 3)
private String imName;
}
导出service类
@Override
public void exportExcel() {
List<CameraExportExcel> dataList = cameraMapper.getCameraExportData();
if (CollUtil.isEmpty(dataList)) {
return;
}
ExcelUtil.writeExcel(this.response, "设备列表", dataList, CameraExportExcel.class);
}