解析接口
public interface IExcelParseService {
/**
* 通用excel解析(只针对单sheet页)
* @param parseDataDto 待解析对象
* @return ParseResult
*/
ParseResult doExcelParse(ParseDataDto parseDataDto);
/**
* 业务类型,区分不同excel解析功能
* @return string
*/
String type();
}
抽象实现、采用模板方法模式
public abstract class AbstractExcelParseService<T, F> implements IExcelParseService {
/**
* Excel 解析分四步进行
* 第一步: 解析Excel 数据
* 第二步: 校验Excel 标题行校验码
* 第三步: 数据校验和转换
* 第四步: 数据入库
*/
@Override
public ParseResult doExcelParse(ParseDataDto parseDataDto) {
if (parseDataDto == null) {
log.error("参数: parseDataDto is null");
return new ParseResult(ParseStatus.EXCEL_NOT_FOUND, "文件未找到 ");
}
// 获取公共文件路径
String commonPath = parseDataDto.getCommPath();
StringBuilder fullPath = new StringBuilder(commonPath);
if (!parseDataDto.getFilePath().startsWith(File.separator)) {
fullPath.append(File.separator);
}
fullPath.append(parseDataDto.getFilePath());
InputStream inputStream;
try {
inputStream = new BufferedInputStream(new FileInputStream(fullPath.toString()));
} catch (FileNotFoundException e) {
log.error("文件" + parseDataDto.getId() + "未找到:" + fullPath);
return new ParseResult(ParseStatus.EXCEL_NOT_FOUND, "文件未找到 ");
}
ExcelData<T> excelData;
try {
excelData = parseExcel(inputStream, getEntityClass());
} catch (Exception e) {
log.error("模板解析错误:" + e);
return new ParseResult(ParseStatus.ERROR, "Excel解析错误");
}
ParseResult parseResult = headCheck(parseDataDto.getType(), excelData);
if (parseResult.getParseStatus() != ParseStatus.SUCCESS) {
return new ParseResult(ParseStatus.TEMPLATE_ERROR, parseResult.getMessage());
}
ExcelResult<F> excelResult = dataCheckAndTrans(excelData, parseDataDto);
return saveData(excelResult, parseDataDto);
}
/**
* 第一步: 解析Excel数据
*/
protected <T> ExcelData<T> parseExcel(InputStream inputStream, Class<T> clazz) {
return EasyExcelUtil.readExcel(inputStream, clazz);
}
/**
* 获取对象类型
*
* @return Class
*/
protected abstract Class<T> getEntityClass();
/**
* 第二步: 校验Excel 标题行和校验码
*
* @param type 业务类型
* @param excelData 数据
* @return ParseResult
*/
protected abstract ParseResult headCheck(String type, ExcelData<T> excelData);
/**
* 第三步: 数据校验和转换
*/
protected abstract ExcelResult<F> dataCheckAndTrans(ExcelData<T> excelData, ParseDataDto parseDataDto);
/**
* 第四步: 数据入库
*/
protected abstract ParseResult saveData(ExcelResult<F> excelResult, ParseDataDto parseDataDto);
/**
* 去除表头最后的 * 号
*
* @param string 表头字段
* @return String
*/
public String wipeLastStar(String string) {
if (StringUtils.equals(StringUtils.right(string, 1), "*")) {
return StringUtils.left(string, string.length() - 1);
}
return string;
}
/**
* 获取需要生成校验码的标题行并拼装成字符串,以逗号分割
* 注意:需要把业务类别放在第一个
*
* @param type
* @param headMap
* @return
*/
protected String getHeadString(String type, Map<Integer, String> headMap) {
Collection<String> heads = headMap.values();
return StringUtils.join(heads, ",");
}
}
路由类,采用策略模式
@Primary
@Service
public class ExcelParseRouteServiceImpl implements IExcelParseService {
private Map<String, IExcelParseService> parseServiceMap;
@Autowired
private List<IExcelParseService> parseServices;
/**
* 初始化类是 将IExcelParseService 的所有实现类放入map容器
*/
@PostConstruct
public void init() {
parseServiceMap = Maps.newConcurrentMap();
for (IExcelParseService excelParseService : parseServices) {
parseServiceMap.put(excelParseService.type(), excelParseService);
}
}
@Override
public ParseResult doExcelParse(ParseDataDto parseDataDto) {
if (parseDataDto == null) {
return new ParseResult(ParseStatus.ERROR, "入参为空");
}
String type = parseDataDto.getType();
IExcelParseService excelParseService = parseServiceMap.get(type);
if (excelParseService == null) {
log.error("业务类别:type = " + type + " 没有配置解析器");
return new ParseResult(ParseStatus.ERROR, "没有配置Excel解析器");
}
return excelParseService.doExcelParse(parseDataDto);
}
@Override
public String type() {
return null;
}
}
待解析对象
/**
* 待解析对象
*/
@Data
public class ParseDataDto {
private String id;
private String type;
private String commPath;
private String filePath;
}
解析结果状态枚举类
/**
* Excel 解析结果状态枚举类
*
*/
public enum ParseStatus {
/**
* 未处理
*/
UNPROCESSED("00"),
/**
* 正在解析
*/
PROCESSING("01"),
/**
* 成功
*/
SUCCESS("11"),
/**
* 解析成功,部分数据有误,请下载模板文件查看错误详情
*/
DATA_ERROR("12"),
/**
* 处理失败
*/
ERROR("21"),
/**
* 不符合模板
*/
TEMPLATE_ERROR("22"),
/**
* 文件未找到
*/
EXCEL_NOT_FOUND("23");
private final String code;
ParseStatus(String code){
this.code=code;
}
public String getCode(){
return code;
}
}
解析结果类
/**
* 导入处理结果
*
*/
@Data
public class ParseResult {
private ParseStatus parseStatus;
private int successCount;
private int errorCount;
private int count;
private String message;
public ParseResult(ParseStatus parseStatus) {
this.parseStatus = parseStatus;
}
public ParseResult(ParseStatus parseStatus, String message) {
this.parseStatus = parseStatus;
this.message = message;
}
}
解析excel的返回值
/**
* 解析excel的返回值
*
*/
@Data
public class ExcelResult<T> {
private int count;
private String message;
private List<T> successData;
private List<T> errorData;
}
excel 数据封装类
/**
* 解析后的excel 数据封装类
*/
@Data
public class ExcelData<T> {
private List<T> dataList;
private Map<Integer, String> headMap;
}
excel 解析监听类
/**
* excel 解析监听类
*/
@Slf4j
public class ExcelDataListener<T> extends AnalysisEventListener<T> {
private final List<T> dataList = new ArrayList<>(2000);
private Map<Integer, String> headMap;
public Map<Integer, String> getHeadMap() {
return headMap;
}
public List<T> getDataList() {
return dataList;
}
@Override
public void invoke(T t, AnalysisContext analysisContext) {
dataList.add(t);
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
/**
* 获取表头信息
*
* @param headMap 表头map
* @param context context
*/
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
this.headMap = headMap;
}
}
EasyExcelUtil 工具类
public class EasyExcelUtil {
/**
* 获取excel表头
* @param heads 标题数组
* @return excel easyExcel标题数组
*/
public static List<List<String>> getHeadList(String... heads) {
List<List<String>> headList = new ArrayList<>(heads.length);
for (String head : heads) {
headList.add(Lists.newArrayList(head)) ;
}
return headList;
}
/**
* 读Excel 返回list 数据
* @param filePath excel 路径
* @return clazz 映射类
*/
public static <T> List<T> read(String filePath, Class<T> clazz) {
ExcelDataListener<T> dataListener = new ExcelDataListener<T>();
EasyExcel.read(filePath, clazz, dataListener).sheet().doRead();
return dataListener.getDataList();
}
/**
* 读Excel 返回list 数据
* @param inputStream excel流
* @return clazz 映射类
*/
public static <T> List<T> read(InputStream inputStream, Class<T> clazz) {
ExcelDataListener<T> dataListener = new ExcelDataListener<T>();
EasyExcel.read(inputStream, clazz, dataListener).sheet().doRead();
return dataListener.getDataList();
}
/**
* 读Excel 返回list 数据和表头数据
* @param filePath excel 路径
* @return clazz 映射类
*/
public static <T> ExcelData<T> readExcel(String filePath, Class<T> clazz) {
ExcelDataListener<T> dataListener = new ExcelDataListener<T>();
EasyExcel.read(filePath, clazz, dataListener).sheet().doRead();
ExcelData<T> excelData = new ExcelData<>();
excelData.setDataList(dataListener.getDataList());
excelData.setHeadMap(dataListener.getHeadMap());
return excelData;
}
/**
* 读Excel 返回list 数据和表头数据
* @param inputStream excel流
* @return clazz 映射类
*/
public static <T> ExcelData<T> readExcel(InputStream inputStream, Class<T> clazz) {
ExcelDataListener<T> dataListener = new ExcelDataListener<T>();
EasyExcel.read(inputStream, clazz, dataListener).sheet().doRead();
ExcelData<T> excelData = new ExcelData<>();
excelData.setDataList(dataListener.getDataList());
excelData.setHeadMap(dataListener.getHeadMap());
return excelData;
}
/**
* web 导出excel
* @param response response
* @param excelName excel名
* @param sheetName sheet名
* @param clazz 映射类
* @param dataList 数据
*/
public static <T> void export(HttpServletResponse response, String excelName, String sheetName,
Class<T> clazz, List<T> dataList) throws IOException {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码
excelName = URLEncoder.encode(excelName, "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + excelName + ".xlsx");
EasyExcel.write(response.getOutputStream(), clazz)
.sheet(sheetName).doWrite(dataList);
}
/**
* web 导出excel
* @param response response
* @param excelName excel名
* @param sheetName sheet名
* @param List<List<String>> headList
* @param dataList 数据
*/
public static <T> void export(HttpServletResponse response, String excelName, String sheetName,
List<List<String>> headList, List<T> dataList) throws IOException {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码
excelName = URLEncoder.encode(excelName, "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + excelName + ".xlsx");
EasyExcel.write(response.getOutputStream()).head(headList)
.sheet(sheetName).doWrite(dataList);
}
}
具体业务类只需要继承抽象实现、实现具体类即可