模板导出
parent
2ee88c7589
commit
32dacef919
@ -0,0 +1,30 @@
|
||||
package cn.jlw.util.excel;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.jlw.util.SpringContextUtil;
|
||||
import com.ibeetl.admin.core.entity.CoreDict;
|
||||
import com.ibeetl.admin.core.service.CoreDictService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class DeviceExcelSelectorServiceImpl implements ExcelSelectorService {
|
||||
|
||||
@Override
|
||||
public String[] getSelectorData() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getSelectorData(String dictKeyValue) {
|
||||
CoreDictService coreDictService = (CoreDictService) SpringContextUtil.getBean(CoreDictService.class);
|
||||
List<CoreDict> list = coreDictService.findAllByTable(dictKeyValue);
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return null;
|
||||
}
|
||||
return list.stream().map(CoreDict::getName).toArray(String[]::new);
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package cn.jlw.util.excel;
|
||||
|
||||
import cn.hutool.core.util.EnumUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* excel的文件格式
|
||||
* </p>
|
||||
*
|
||||
* @author mlx
|
||||
* @date 2022/11/26
|
||||
* @modified
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum EasyExcelTypeEnum {
|
||||
|
||||
CSV(".csv"),
|
||||
XLS(".xls"),
|
||||
XLSX(".xlsx");
|
||||
|
||||
private String fileExtName;
|
||||
|
||||
public static EasyExcelTypeEnum parseType(String fileExtName) {
|
||||
return EnumUtil.likeValueOf(EasyExcelTypeEnum.class, fileExtName);
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package cn.jlw.util.excel;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.excel.context.AnalysisContext;
|
||||
import com.alibaba.excel.exception.ExcelDataConvertException;
|
||||
import com.alibaba.excel.read.listener.ReadListener;
|
||||
import com.alibaba.excel.read.metadata.holder.ReadRowHolder;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@Slf4j
|
||||
public class ExcelCellDataListener<T> implements ReadListener<T> {
|
||||
|
||||
/**
|
||||
* 数据集合
|
||||
*/
|
||||
private List<T> data = CollUtil.newArrayList();
|
||||
|
||||
private Map<Integer, ExcelFailRecord> failMap = MapUtil.newHashMap(16);
|
||||
|
||||
@Override
|
||||
public void invoke(T bean, AnalysisContext context) {
|
||||
boolean emptyRow = true;
|
||||
List<Field> fieldList = Arrays.asList(ReflectUtil.getFields(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");
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package cn.jlw.util.excel;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* excel导入的错误信息记录
|
||||
* </p>
|
||||
*
|
||||
* @author mlx
|
||||
* @date 2022/11/26
|
||||
* @modified
|
||||
*/
|
||||
@Data
|
||||
public class ExcelFailRecord {
|
||||
|
||||
private Integer row;
|
||||
private Integer column;
|
||||
private String failMessage;
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package cn.jlw.util.excel;
|
||||
|
||||
public @interface ExcelSelector {
|
||||
|
||||
/**
|
||||
* 固定数据
|
||||
*/
|
||||
String[] fixedSelector() default {};
|
||||
|
||||
/**
|
||||
* 字典key
|
||||
*/
|
||||
String dictKeyValue() default "";
|
||||
|
||||
/**
|
||||
* 服务类
|
||||
*/
|
||||
Class<? extends ExcelSelectorService>[] serviceClass() default {};
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package cn.jlw.util.excel;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.alibaba.excel.write.handler.SheetWriteHandler;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
|
||||
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
|
||||
import lombok.Data;
|
||||
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 java.util.Map;
|
||||
|
||||
@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);
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package cn.jlw.util.excel;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Excel选择器解析
|
||||
* @author lx
|
||||
*/
|
||||
@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 mlx
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package cn.jlw.util.excel;
|
||||
|
||||
public interface ExcelSelectorService {
|
||||
|
||||
/**
|
||||
* 获取下拉数据
|
||||
*
|
||||
* @return java.lang.String[]
|
||||
* @author mlx
|
||||
*/
|
||||
String[] getSelectorData();
|
||||
|
||||
/**
|
||||
* 根据字典key获取下拉数据
|
||||
*
|
||||
* @param dictKeyValue 字典key
|
||||
* @return java.lang.String[]
|
||||
* @author mlx
|
||||
*/
|
||||
String[] getSelectorData(String dictKeyValue);
|
||||
}
|
@ -0,0 +1,282 @@
|
||||
package cn.jlw.util.excel;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.alibaba.excel.ExcelReader;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.alibaba.excel.read.listener.ReadListener;
|
||||
import com.alibaba.excel.read.metadata.ReadSheet;
|
||||
import com.alibaba.excel.support.ExcelTypeEnum;
|
||||
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
|
||||
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
|
||||
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
|
||||
import com.ibeetl.admin.core.util.FreezeAndFilter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.poi.ss.usermodel.HorizontalAlignment;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@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 mlx
|
||||
*/
|
||||
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 mlx
|
||||
*/
|
||||
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)
|
||||
.registerWriteHandler(new FreezeAndFilter())
|
||||
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度
|
||||
.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 mlx
|
||||
*/
|
||||
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 mlx
|
||||
*/
|
||||
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 mlx
|
||||
*/
|
||||
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 mlx
|
||||
*/
|
||||
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 mlx
|
||||
*/
|
||||
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 mlx
|
||||
*/
|
||||
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 mlx
|
||||
*/
|
||||
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 mlx
|
||||
*/
|
||||
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 mlx
|
||||
*/
|
||||
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 mlx
|
||||
*/
|
||||
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<java.lang.Integer, cn.com.zxelec.common.resolve.ExcelSelectorResolve>
|
||||
* @author mlx
|
||||
*/
|
||||
private static Map<Integer, ExcelSelectorResolve> getSelectedMap(Class<?> clazz) {
|
||||
Map<Integer, ExcelSelectorResolve> selectedMap = MapUtil.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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue