运用EasyExcel 导入数据,失败原由数据导出
<h1 style="color: black; text-align: left; margin-bottom: 10px;">引言</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">在<span style="color: black;">平常</span><span style="color: black;">研发</span>过程中,Excel 导入是非常<span style="color: black;">平常</span>的场景,<span style="color: black;">况且</span><span style="color: black;">亦</span>有<span style="color: black;">非常多</span>开源的项目是针对Excel的读写的,如Apache 的poi ,<span style="color: black;">近期</span>用的比较好的还是阿里的EasyExcel 开源工具。平时<span style="color: black;">咱们</span>只是简单的读取文件并写入数据库持久化<span style="color: black;">就可</span>,<span style="color: black;">然则</span>前段时间,<span style="color: black;">制品</span>搞了个<span style="color: black;">需要</span>,需要将导入失败的数据及<span style="color: black;">原由</span>写入Excel并下载,那这就有得玩了,废话不多说,上才艺。</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">制品</span><span style="color: black;">需要</span></h1><span style="color: black;">导入Excel数据</span><span style="color: black;">数据格式校验</span><span style="color: black;">数据合法性校验(校验数据库)</span><span style="color: black;">失败数据<span style="color: black;">供给</span>用户下载,并支持再次导入</span>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">技术选型</h1><span style="color: black;">https://github.com/alibaba/easyexcel</span><span style="color: black;"> ,Excel 读取/写入</span><span style="color: black;">https://www.xuxueli.com/xxl-job/</span><span style="color: black;"> ,做异步处理</span>
<h1 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">需要</span>实现</h1>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">项目依赖(maven)</h1><span style="color: black;"><!-- easyexcle --></span>
<span style="color: black;"><<span style="color: black;">dependency</span>></span>
<span style="color: black;"><<span style="color: black;">groupId</span>></span>com.alibaba<span style="color: black;"></<span style="color: black;">groupId</span>></span>
<span style="color: black;"><<span style="color: black;">artifactId</span>></span>easyexcle<span style="color: black;"></<span style="color: black;">artifactId</span>></span>
<span style="color: black;"><<span style="color: black;">version</span>></span>2.2.6<span style="color: black;"></<span style="color: black;">version</span>></span>
<span style="color: black;"></<span style="color: black;">dependency</span>></span>
<span style="color: black;"><!-- xxl job --></span>
<span style="color: black;"><<span style="color: black;">dependency</span>></span>
<span style="color: black;"><<span style="color: black;">groupId</span>></span>com.xuxueli<span style="color: black;"></<span style="color: black;">groupId</span>></span>
<span style="color: black;"><<span style="color: black;">artifactId</span>></span>xxl-job-core<span style="color: black;"></<span style="color: black;">artifactId</span>></span>
<span style="color: black;"><<span style="color: black;">version</span>></span>${xxl-job.version}<span style="color: black;"></<span style="color: black;">version</span>></span>
<span style="color: black;"></<span style="color: black;">dependency</span>></span>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">文件解析</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">解析导入文件,获取文件数据量,用于判定导入<span style="color: black;">是不是</span>走异步导入。</span></p><span style="color: black;">public</span> <span style="color: black;"><span style="color: black;">class</span> <span style="color: black;">EasyExcelUtils</span> </span>{
<span style="color: black;">/**
*
* 解析文件,获取最后一行
* <span style="color: black;">@param</span> inputStream 文件流
* <span style="color: black;">@param</span>sheetNum 读取excel表格的sheetNum 索引
*<span style="color: black;">@return</span> 总行数
*/</span>
<span style="color: black;"><span style="color: black;">public</span> <span style="color: black;">static</span> Integer <span style="color: black;">lastNum</span><span style="color: black;">(InputStream inputStream,Integer sheetNum)</span></span>{
Workbook wb = <span style="color: black;">null</span>;
sheetNum = sheetNum == <span style="color: black;">null</span> ? <span style="color: black;">0</span> : sheetNum;
<span style="color: black;">try</span>{
wb = WorkbookFactory.create(inputStream);
Sheet sheet = wb.getSheetAt(sheetNum);
CellReference cellReference =<span style="color: black;">new</span> CellReference(<span style="color: black;">"A4"</span>);
<span style="color: black;">// 处理空行</span>
<span style="color: black;">for</span> (<span style="color: black;">int</span> i = cellReference.getRow();i <= sheet.getLastRowNum();){
<span style="color: black;">// 省略部分代码</span>
}
<span style="color: black;">return</span>sheet.getLastRowNum();
}<span style="color: black;">catch</span> (Exception e){
}
<span style="color: black;">return</span> <span style="color: black;">0</span>;
}
}<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">判定导入数据文件<span style="color: black;">是不是</span>为空,<span style="color: black;">倘若</span>为空,将返回错误信息</span></p><span style="color: black;">@RestController</span>
<span style="color: black;">// 省略其他注解</span>
<span style="color: black;">public</span> <span style="color: black;"><span style="color: black;">class</span> <span style="color: black;">ProjectInfoController</span> </span>{
<span style="color: black;">/**
* 项目信息导入
*/</span>
<span style="color: black;">@PostMapping</span>(<span style="color: black;">"/import"</span>)
<span style="color: black;"><span style="color: black;">public</span> R <span style="color: black;">projectInfoImport</span><span style="color: black;">(MultipartFile file,HttpServletResponse response)</span></span>{
InputStream inputStream = <span style="color: black;">null</span>;
<span style="color: black;">int</span> lastNum = <span style="color: black;">0</span>;
<span style="color: black;">try</span> {
lastNum = EasyExcelUtils.lastNum(file.getInputStream());
}<span style="color: black;">catch</span>(IOException e){
<span style="color: black;">// 省略部分代码</span>
}
<span style="color: black;">if</span> (lastNum <= <span style="color: black;">0</span> ){
<span style="color: black;">throw</span>CustomExcetpoin(<span style="color: black;">500</span>,<span style="color: black;">"导入文件数据为空,请重新上传"</span>);
}
}
}<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">文件解析拿到导入数据的数据量,与系统配置的文件导入上限值进行判定,<span style="color: black;">倘若</span>大于上限值将走异步处理(异步导入,请查看异步“异步导入”导入内容)。</span></p><span style="color: black;">@RestController</span>
<span style="color: black;">// 省略其他注解</span>
<span style="color: black;">public</span> <span style="color: black;"><span style="color: black;">class</span> <span style="color: black;">ProjectInfoController</span> </span>{
<span style="color: black;">@Resource</span>
<span style="color: black;">private</span> AsyncExcelService asyncExcelService;
<span style="color: black;">/**
* 项目信息导入
*/</span>
<span style="color: black;">@PostMapping</span>(<span style="color: black;">"/import"</span>)
<span style="color: black;"><span style="color: black;">public</span> R <span style="color: black;">projectInfoImport</span><span style="color: black;">(MultipartFile file,HttpServletResponse response)</span></span>{
InputStream inputStream = <span style="color: black;">null</span>;
<span style="color: black;">int</span> lastNum = <span style="color: black;">0</span>;
<span style="color: black;">try</span>{
lastNum = EasyExcelUtils.lastNum(file.getInputStream());
}<span style="color: black;">catch</span>(IOException e){
<span style="color: black;">// 省略部分代码</span>
}
<span style="color: black;">if</span> (lastNum <= <span style="color: black;">0</span> ){
<span style="color: black;">throw</span> CustomExcetpoin(<span style="color: black;">500</span>,<span style="color: black;">"导入文件数据为空,请重新上传"</span>);
}
<span style="color: black;">// 获取系统配置的导入上限值</span>Integer importMax = asyncExcelService.asyncProjectImportMax();<span style="color: black;">if</span> (lastNum > importMax ){
<span style="color: black;">// 达到上限,走异步</span>
asyncExcelService.asyncProjectImport(file,response);
<span style="color: black;">return</span> R.success(<span style="color: black;">"数据导入成功,因数据量比<span style="color: black;">很强</span>,已转为异步导入"</span>);
}
<span style="color: black;">// 省略其他代码</span>
}
}<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">AsyncExcelService 接口实现</span></p><span style="color: black;">/**
* 异步导出/导入 service
*/</span>
<span style="color: black;">public</span> <span style="color: black;"><span style="color: black;">interface</span> <span style="color: black;">AsyncExcelService</span> </span>{
<span style="color: black;">/** 默认导入数据上限 **/</span>
Integer DEFAULT_IMPORT_DATA_MAX = <span style="color: black;">500</span>;
<span style="color: black;">/**
* 获取最大导入上限值,超过则走异步
*/</span>
<span style="color: black;">Integer <span style="color: black;">getImportMax</span><span style="color: black;">()</span></span>;
<span style="color: black;">/**
* 异步导入数据
*/</span>
<span style="color: black;"><span style="color: black;">void</span> <span style="color: black;">asyncProjectImport</span><span style="color: black;">(MultipartFile file,HttpServletResponse response)</span></span>;
}
<span style="color: black;">@Service</span>
<span style="color: black;">// 省略其他注解</span>
<span style="color: black;">public</span> <span style="color: black;"><span style="color: black;">class</span> <span style="color: black;">AsyncExcelServiceImpl</span> <span style="color: black;">implements</span> <span style="color: black;">AsyncExcelService</span> </span>{
<span style="color: black;">@Resource</span>
<span style="color: black;">private</span> IParamtersClient paramtersClient;
<span style="color: black;">@Override</span>
<span style="color: black;"><span style="color: black;">public</span> Integer <span style="color: black;">getImportMax</span><span style="color: black;">()</span></span>{
Integer value = getParamVaule(<span style="color: black;">"paramName"</span>,Integer<span style="color: black;">.<span style="color: black;">class</span>)</span>;
<span style="color: black;">return</span>value ==<span style="color: black;">null</span> ? DEFAULT_IMPORT_DATA_MAX : value;
}
<span style="color: black;">/**
* 调用框架接口获取系统参数
*
*/</span>
<span style="color: black;">private</span> <T> <span style="color: black;">T <span style="color: black;">getParamVaule</span><span style="color: black;">(String name,Class<T> clazz)</span></span>{
CCBHousingUser user = SecureUtil.getUser();<span style="color: black;">// 省略部分代码</span>
<span style="color: black;">// 获取系统配置参数</span>Parameters parameters = paramtersClient.getParamterByCodeAndOrg(name,user.getOrganizationId());<span style="color: black;">// 省略部分代码</span>
}
}<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">其中,IParamtersClient 属于框架<span style="color: black;">供给</span>的feign 接口,<span style="color: black;">亦</span><span style="color: black;">能够</span><span style="color: black;">按照</span>自己的<span style="color: black;">实质</span>场景实现<span style="color: black;">关联</span><span style="color: black;">规律</span>。</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">数据合法校验</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">导入数据文件解析<span style="color: black;">运用</span>的是alibaba <span style="color: black;">供给</span>的 EasyExcel 开源工具,<span style="color: black;">咱们</span>需要在 EasyExcel 工具的<span style="color: black;">基本</span>上做<span style="color: black;">有些</span><span style="color: black;">加强</span>处理,如:导入格式校验、导入表头校验、导入数据格式校验等,<span style="color: black;">倘若</span><span style="color: black;">出现</span>校验失败,将错误信息写入错误报告(excel)输出到客户端。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">定义easyexcel 导入文件到列与实体映射关系,将<span style="color: black;">运用</span>到 easyexcel 到@ExcleProperty 注解进行关系绑定</span></p><span style="color: black;">@Data</span>
<span style="color: black;">// 省略其他注解</span>
<span style="color: black;">public</span> <span style="color: black;"><span style="color: black;">class</span> <span style="color: black;">ProjectInfoExcelDTO</span> </span>{
<span style="color: black;">@ExcelProperty(index=0,value=<span style="color: black;">"序列号"</span>)</span>
<span style="color: black;">private</span> String number;
<span style="color: black;">@ExcelProperty(index=1,value=<span style="color: black;">"项目名<span style="color: black;">叫作</span>"</span>)</span>
<span style="color: black;">private</span> String name;
<span style="color: black;">// 省略其他字段属性</span>
}<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">注解 @ExcleProperty 常用属性</span></p><span style="color: black;">index,与excel文件中,表头列的索引位置对应(从0<span style="color: black;">起始</span>)</span><span style="color: black;">value,与excel文件中,表头列的名<span style="color: black;">叫作</span>相对应</span><span style="color: black;">converter,指定解析数据时,该列需要<span style="color: black;">运用</span>的数据转换器,转换器实现Converter接口</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">定义校验错误的数据结构类型</span></p>@Data
<span style="color: black;">// 省略其他注解</span>
<span style="color: black;">public</span> <span style="color: black;"><span style="color: black;">class</span> <span style="color: black;">ExcelChcekErrDTO</span><T> {</span>
<span style="color: black;">private</span> T t;
<span style="color: black;">private</span> String errMsg;
}<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">备注:@Data 属于 lombok 工具,简化Bean的封装,感兴趣的<span style="color: black;">朋友</span>,<span style="color: black;">能够</span><span style="color: black;">自动</span>查阅资料。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">定义Excel导入校验返回的数据VO</span></p>@Data
<span style="color: black;">// 省略其他注解</span>
<span style="color: black;">public</span> <span style="color: black;"><span style="color: black;">class</span> <span style="color: black;">ExcelCheckResultVO</span><<span style="color: black;">T</span>> </span>{
<span style="color: black;">/** 校验成功的数据 **/</span>
<span style="color: black;">private</span> <span style="color: black;">List</span><T> successDatas;
<span style="color: black;">/** 校验失败的数据 **/</span>
<span style="color: black;">private</span> <span style="color: black;">List</span><ExcelChcekErrDTO> errData;
}<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">定义数据解析监听器EasyExcelListener</span></p><span style="color: black;">@Data</span>
<span style="color: black;">// 省略部分注解</span>
<span style="color: black;">public</span> <span style="color: black;"><span style="color: black;">class</span> <span style="color: black;">EasyExcelListener</span><<span style="color: black;">T</span>> <span style="color: black;">extends</span> <span style="color: black;">AnalysisEventListener</span><<span style="color: black;">T</span>> </span>{
<span style="color: black;">// 省略部分代码</span>
}<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">定义excel 业务校验管理器 ExcelCheckManager,需要做业务校验的(与数据库匹配等)需要实现该接口</span></p><span style="color: black;">public</span> <span style="color: black;"><span style="color: black;">interface</span> <span style="color: black;">ExcelCheckManager</span><<span style="color: black;">T</span>> </span>{
<span style="color: black;">ExcelCheckResultVO <span style="color: black;">checkImportExcle</span><span style="color: black;">(List<T> datas)</span></span>;
}<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;"><span style="color: black;">表头校验</span></strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">运用</span>EasyExcelListener 用来监听数据解析过程,其中,invokHeadMap <span style="color: black;">办法</span>将在解析完成excel表头时将被执行</span></p><span style="color: black;">@Data</span>
<span style="color: black;">// 省略部分注解</span>
<span style="color: black;">public</span> <span style="color: black;"><span style="color: black;">class</span> <span style="color: black;">EasyExcelListener</span><<span style="color: black;">T</span>> <span style="color: black;">extends</span> <span style="color: black;">AnalysisEventListener</span><<span style="color: black;">T</span>> </span>{
<span style="color: black;">/** excel 对象的反射类 **/</span>
<span style="color: black;">private</span> Class<T> clazz;
<span style="color: black;">private</span> ExcelCheckManager<T> excelCheckManager;
<span style="color: black;"><span style="color: black;">public</span> <span style="color: black;">EasyExcelListener</span><span style="color: black;">(ExcelCheckManager<T> excelCheckManager,Class<T> clazz)</span></span>{
<span style="color: black;">this</span>.clazz = clazz;
<span style="color: black;">this</span>.excelCheckManager = excelCheckManager;
}<span style="color: black;">@Override</span>
<span style="color: black;"><span style="color: black;">public</span> <span style="color: black;">void</span> <span style="color: black;">invokHeadMap</span><span style="color: black;">(Map<Integer,String> headMap,AnalysisContext context)</span></span>{
<span style="color: black;">super</span>.invokHeadMap(headMap,context);<span style="color: black;">// 反射获取实体到属性值</span>
Map<Integer,String> indexNameMap = getIndexNameMap(clazz);
<span style="color: black;">// 将 headMap 与 indexNameMap 进行对比,<span style="color: black;">是不是</span>完全匹配</span>Set<Integer> keySet = indexNameMap.keySet();<span style="color: black;">for</span> (Integer key : keySet ){
<span style="color: black;">if</span> (StringUtils.isEmpty(headMap.get(key)){
<span style="color: black;">throw</span> ExcelAnalysisExcetpion(<span style="color: black;">"数据解析错误,请传入正确的excel格式"</span>);
}
<span style="color: black;">if</span>(!headMap.get(key).equals(indexNameMap.get(key)){<span style="color: black;">throw</span> ExcelAnalysisExcetpion(<span style="color: black;">"数据解析错误,请传入正确的excel格式"</span>);
}
}
}
<span style="color: black;">/**
* 反射获取解析数据实体的<span style="color: black;">@ExcleProperty</span>的value
*/</span>
<span style="color: black;"><span style="color: black;">public</span> Map<Integer,String> <span style="color: black;">getIndexNameMap</span><span style="color: black;">(Class clazz)</span></span>{
Map<Integer,String> result = <span style="color: black;">new</span>HashMap<>();
Field field;
Field[] fields = clazz.getDeclaredFields();<span style="color: black;">for</span> (<span style="color: black;">int</span> i = <span style="color: black;">0</span>; i < fields.length; i++){
field = clazz.getDeclaredField(fields.getName());
field.setAccessible(<span style="color: black;">true</span>);
ExcelProperty excleProperty = field.getAnnotation(ExcelProperty<span style="color: black;">.<span style="color: black;">class</span>)</span>;
<span style="color: black;">if</span> (excelProperty != <span style="color: black;">null</span>){
<span style="color: black;">int</span>index = excleProperty.index();
String[] values = excleProperty.value();
StringBuilder value =<span style="color: black;">new</span> StringBuilder();
<span style="color: black;">for</span>(String v : values ){
value.append(v);
}
result.put(index,value.toString());
}
}<span style="color: black;">return</span> result;
}
}<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;"><span style="color: black;">数据非空、格式校验</span></strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">数据非空校验、格式校验,<span style="color: black;">咱们</span>将<span style="color: black;">运用</span>hibernate-validator 校验器进行校验格式。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">定义validator 工具类</span></p><span style="color: black;">@component</span>
<span style="color: black;">public</span> <span style="color: black;"><span style="color: black;">class</span> <span style="color: black;">EasyExcelValidatorHelper</span> </span>{
<span style="color: black;">private</span> static Validtor validtor;
<span style="color: black;">@Autowired</span>
<span style="color: black;">public</span> EasyExcelValidatorHelper(Validtor validtor){
<span style="color: black;">this</span>.EasyExcelValidatroHelper.validtor = validtor;
}<span style="color: black;">public</span> static <T> String validateEntity(T obj) throws NoSuchFieldException{
StringBuilder result = new StringBuilder();
<span style="color: black;">// 执行校验</span>
Set<ConstraionViolation<T>> <span style="color: black;">set</span> = validtor.validate(obj,Default<span style="color: black;">.<span style="color: black;">class</span>);</span>
<span style="color: black;">// 组装结果</span>
<span style="color: black;">if</span>(<span style="color: black;">set</span> != <span style="color: black;">null</span> && !<span style="color: black;">set</span>.isEmpty()){
<span style="color: black;">for</span> (ConstraionViolation<T> cv : <span style="color: black;">set</span>){
Field declaredField = obj.getClass.getDeclaredField(cv.getPropertiyPath().toString());
ExcelProperty<span style="color: black;">annotation</span>= declaredField.getAnnotation(ExcelProperty<span style="color: black;">.<span style="color: black;">class</span>);</span>
result.append(<span style="color: black;">annotation</span>.value[<span style="color: black;">0</span>]+<span style="color: black;">":"</span>+cv.getMessage()).append(<span style="color: black;">";"</span>);
}
}
<span style="color: black;">return</span> result;
}
}<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">数据格式校验,<span style="color: black;">运用</span>EasyExcelListener 用来监听数据解析过程,其中,invok <span style="color: black;">办法</span>将逐行解析excel数据的时候将被调用</span></p><span style="color: black;">@Data</span>
<span style="color: black;">// 省略部分注解</span>
<span style="color: black;">public</span> <span style="color: black;">class</span> EasyExcelListener<T> <span style="color: black;">extends</span> AnalysisEventListener<T> {
<span style="color: black;">/** 标记<span style="color: black;">是不是</span>执行数据解析 **/</span>
<span style="color: black;">private</span> <span style="color: black;">boolean</span> baseMatching = <span style="color: black;">false</span>;
<span style="color: black;">/** 解析成功的数据 **/</span>
<span style="color: black;">private</span> List<T> successList = <span style="color: black;">new</span> ArrayList<>();
<span style="color: black;">/** 解析失败的数据 **/</span>
<span style="color: black;">private</span> List<ExcelCheckErrDTO<T>> errList = <span style="color: black;">new</span>ArrayList<>();<span style="color: black;">/** excel 对象的反射类 **/</span>
<span style="color: black;">private</span> Class<T> clazz;
<span style="color: black;">private</span> List<T> list;
<span style="color: black;">private</span> ExcelCheckManager<T> excelCheckManager;
<span style="color: black;">public</span>EasyExcelListener(ExcelCheckManager<T> excelCheckManager,Class<T> clazz){<span style="color: black;">this</span>.clazz = clazz;
<span style="color: black;">this</span>.excelCheckManager = excelCheckManager;
}
<span style="color: black;">@Override</span>
<span style="color: black;">public</span> <span style="color: black;">void</span>invok(T t,AnalysisContext context){<span style="color: black;">// 数据解析/转换完成,标记进入到解析起</span>
baseMatching = <span style="color: black;">true</span>;
<span style="color: black;">String</span> errMsg;
<span style="color: black;">try</span> {
<span style="color: black;">// 调用验证器验证数据格式</span>
errMsg = EasyExcelValidatorHelper.validateEntity(t);
}<span style="color: black;">catch</span>(Exception e){
errMsg =<span style="color: black;">"解析数据出错"</span>;
<span style="color: black;">// 省略部分代码</span>
}
<span style="color: black;">// 校验不<span style="color: black;">经过</span></span>
<span style="color: black;">if</span> (!StringUtils.isEmpty(errMsg){
<span style="color: black;">// 将错误数据放入错误列表中</span>
ExcelChcekErrDTO errDTO = <span style="color: black;">new</span>ExcelChcekErrDTO(t,errMsg);
errList.add(errDTO);
}<span style="color: black;">else</span>{
<span style="color: black;">// 校验成功</span>
list.add(t);
}
<span style="color: black;">if</span> (list.size() > <span style="color: black;">1000</span>){
<span style="color: black;">// 业务校验</span>ExcelCheckResultVO excelCheckResultVO = excelCheckManager.checkImportExcel(list);
successList.addAll(excelCheckResultVO.getSuccessDatas());
errList.addAll(excelCheckResultVO.getErrDatas());
list.clear();
}
}<span style="color: black;">/**
* 所有数据解析完成后调用此<span style="color: black;">办法</span>
*/</span>
<span style="color: black;">@Override</span>
<span style="color: black;">public</span> <span style="color: black;">void</span>doAfterAllAnalysed(AnalysisContext context){
ExcelCheckResultVO excelCheckResultVO = excelCheckManager.checkImportExcel(list);
successList.addAll(excelCheckResultVO.getSuccessDatas());
errList.addAll(excelCheckResultVO.getErrDatas());
list.clear();
}<span style="color: black;">@Override</span>
<span style="color: black;">public</span> <span style="color: black;">void</span> invokHeadMap(Map<Integer,<span style="color: black;">String</span>> headMap,AnalysisContext context){
<span style="color: black;">super</span>.invokHeadMap(headMap,context);<span style="color: black;">// 反射获取实体到属性值</span>
Map<Integer,<span style="color: black;">String</span>> indexNameMap = getIndexNameMap(clazz);
<span style="color: black;">// 将 headMap 与 indexNameMap 进行对比,<span style="color: black;">是不是</span>完全匹配</span>Set<Integer> keySet = indexNameMap.keySet();<span style="color: black;">for</span> (Integer key : keySet ){
<span style="color: black;">if</span> (StringUtils.isEmpty(headMap.get(key)){
<span style="color: black;">throw</span> ExcelAnalysisExcetpion(<span style="color: black;">"数据解析错误,请传入正确的excel格式"</span>);
}
<span style="color: black;">if</span>(!headMap.get(key).equals(indexNameMap.get(key)){
<span style="color: black;">throw</span> ExcelAnalysisExcetpion(<span style="color: black;">"数据解析错误,请传入正确的excel格式"</span>);
}
}
}
<span style="color: black;">/**
* 反射获取解析数据实体的@ExcleProperty 的value
*/</span>
<span style="color: black;">public</span> Map<Integer,<span style="color: black;">String</span>> getIndexNameMap(Class clazz){
Map<Integer,<span style="color: black;">String</span>> result = <span style="color: black;">new</span>HashMap<>();
Field field;
Field[] fields = clazz.getDeclaredFields();<span style="color: black;">for</span> (int i = <span style="color: black;">0</span>; i < fields.length; i++){
field = clazz.getDeclaredField(fields.getName());
field.setAccessible(<span style="color: black;">true</span>);
ExcelProperty excleProperty = field.getAnnotation(ExcelProperty.class);
<span style="color: black;">if</span> (excelProperty != <span style="color: black;">null</span>){
int index = excleProperty.index();<span style="color: black;">String</span>[] values = excleProperty.value();
StringBuilder value = <span style="color: black;">new</span> StringBuilder();
<span style="color: black;">for</span> (<span style="color: black;">String</span>v : values ){
value.append(v);
}
result.put(index,value.toString());
}
}<span style="color: black;">return</span> result;
}
}<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">对需要进行校验对字段添加注解</span></p><span style="color: black;">@Data</span>
<span style="color: black;">// 省略其他注解</span>
public class ProjectInfoExcelDTO {
<span style="color: black;">@ExcelProperty</span>(index=<span style="color: black;">0</span>,value=<span style="color: black;">"序列号"</span>)
private String number;
<span style="color: black;">@ExcelProperty</span>(index=<span style="color: black;">1</span>,value=<span style="color: black;">"项目名<span style="color: black;">叫作</span>"</span>)
<span style="color: black;">@NotBlank</span>(message = <span style="color: black;">"请填写项目名<span style="color: black;">叫作</span>"</span>)
private String name;
<span style="color: black;">// 省略其他字段属性</span>
}<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">validator 常用注解传送门(</span><span style="color: black;">validator 常用注解</span><span style="color: black;">)。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">EasyExcel 读取数据,并调用格式校验</span></p><span style="color: black;">@RestController</span>
<span style="color: black;">// 省略其他注解</span>
<span style="color: black;">public</span> <span style="color: black;"><span style="color: black;">class</span> <span style="color: black;">ProjectInfoController</span> </span>{
<span style="color: black;">@Resource</span>
<span style="color: black;">private</span>AsyncExcelService asyncExcelService;<span style="color: black;">@Resource</span>
<span style="color: black;">private</span> ProjectInfoService projectInfoService;
<span style="color: black;">/**
* 项目信息导入
*/</span>
<span style="color: black;">@PostMapping</span>(<span style="color: black;">"/import"</span>)
<span style="color: black;"><span style="color: black;">public</span> R <span style="color: black;">projectInfoImport</span><span style="color: black;">(MultipartFile file,HttpServletResponse response)</span></span>{
InputStream inputStream = <span style="color: black;">null</span>;
<span style="color: black;">int</span> lastNum = <span style="color: black;">0</span>;
<span style="color: black;">try</span>{
lastNum = EasyExcelUtils.lastNum(file.getInputStream());
}<span style="color: black;">catch</span>(IOException e){
<span style="color: black;">// 省略部分代码</span>
}
<span style="color: black;">if</span> (lastNum <= <span style="color: black;">0</span> ){
<span style="color: black;">throw</span> CustomExcetpoin(<span style="color: black;">500</span>,<span style="color: black;">"导入文件数据为空,请重新上传"</span>);
}
<span style="color: black;">// 获取系统配置的导入上限值</span>Integer importMax = asyncExcelService.asyncProjectImportMax();<span style="color: black;">if</span> (lastNum > importMax ){
<span style="color: black;">// 达到上限,走异步</span>
asyncExcelService.asyncProjectImport(file,response);
<span style="color: black;">return</span> R.success(<span style="color: black;">"数据导入成功,因数据量比<span style="color: black;">很强</span>,已转为异步导入"</span>);
}
<span style="color: black;">// 省略部分代码</span>
<span style="color: black;">// 实例数据解析监听器</span>
EasyExcelListener<ProjectInfoDTO> easyExcleListener = <span style="color: black;">new</span>EasyExcelListener(projectInfoService,ProjectInfoDTO<span style="color: black;">.<span style="color: black;">class</span>)</span>;
<span style="color: black;">// 文件读取/解析,并注册监听器</span>
EasyExcle.read(file.getInputStream(),ProjectInfoDTO<span style="color: black;">.<span style="color: black;">class</span>,<span style="color: black;">easyExcleListener</span>).<span style="color: black;">sheet</span>(1).<span style="color: black;">doRead</span>()</span>;
<span style="color: black;">// 获取错误数据</span>List<ExcelCheckErrDTO<ProjectInfoExcelDTO>> errList = easyExcleListener.getErrList();<span style="color: black;">// 获取解析成功到数据</span>List<ProjectinfoExcelDTO> successList = easyExcleListener.getSuccessList();<span style="color: black;">// <span style="color: black;">倘若</span>错误数据不为空,将错误数据写入到excel文件,并输出到浏览器</span>
<span style="color: black;">// 省略代码</span>
<span style="color: black;">// 将成功到数据,批量写入到数据库中</span>
<span style="color: black;">// 省略代码</span>
<span style="color: black;">// 省略其他代码</span>
}
}<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">ProjectInfoService 声明与实现,<span style="color: black;">由于</span>需要做业务数据到校验,<span style="color: black;">因此呢</span>ProjectInfoService 需要继承 ExcelCheckManager 验证管理器</span></p><span style="color: black;">public</span> <span style="color: black;"><span style="color: black;">interface</span> <span style="color: black;">ProjectInfoService</span> <span style="color: black;">extends</span> <span style="color: black;">ExcelCheckManager</span></span>{
}
<span style="color: black;">@Service</span>
<span style="color: black;">// 省略其他注解</span>
<span style="color: black;">public</span> <span style="color: black;"><span style="color: black;">class</span> <span style="color: black;">ProjectInfoServiceImpl</span> <span style="color: black;">implements</span> <span style="color: black;">ProjectInfoService</span> </span>{
<span style="color: black;">// 省略部分代码</span>
<span style="color: black;">@Override</span>
<span style="color: black;"><span style="color: black;">public</span> ExcelCheckResultVO <span style="color: black;">checkImportExcel</span><span style="color: black;">(List<ProjectInfoExcelDTO> datas)</span></span>{
<span style="color: black;">// 省略代码</span>
}
}<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;"><span style="color: black;">输出错误报告</span></strong></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">文件校验完成之后,<span style="color: black;">倘若</span><span style="color: black;">无</span>完全<span style="color: black;">经过</span>,需要将错误对数据以及错误信息<span style="color: black;">经过</span>easyExcel 输出到客户端。</span></p><span style="color: black;">@RestController</span>
<span style="color: black;">// 省略其他注解</span>
<span style="color: black;">public</span> <span style="color: black;"><span style="color: black;">class</span> <span style="color: black;">ProjectInfoController</span> </span>{
<span style="color: black;">@Resource</span>
<span style="color: black;">private</span> AsyncExcelService asyncExcelService;
<span style="color: black;">@Resource</span>
<span style="color: black;">private</span> ProjectInfoService projectInfoService;
<span style="color: black;">/**
* 项目信息导入
*/</span>
<span style="color: black;">@PostMapping</span>(<span style="color: black;">"/import"</span>)
<span style="color: black;"><span style="color: black;">public</span> R <span style="color: black;">projectInfoImport</span><span style="color: black;">(MultipartFile file,HttpServletResponse response)</span></span>{
InputStream inputStream = <span style="color: black;">null</span>;
<span style="color: black;">int</span> lastNum = <span style="color: black;">0</span>;
<span style="color: black;">try</span> {
lastNum = EasyExcelUtils.lastNum(file.getInputStream());
}<span style="color: black;">catch</span>(IOException e){
<span style="color: black;">// 省略部分代码</span>
}
<span style="color: black;">if</span> (lastNum <= <span style="color: black;">0</span> ){
<span style="color: black;">throw</span> CustomExcetpoin(<span style="color: black;">500</span>,<span style="color: black;">"导入文件数据为空,请重新上传"</span>);
}
<span style="color: black;">// 获取系统配置的导入上限值</span>Integer importMax = asyncExcelService.asyncProjectImportMax();<span style="color: black;">if</span> (lastNum > importMax ){
<span style="color: black;">// 达到上限,走异步</span>
asyncExcelService.asyncProjectImport(file,response);
<span style="color: black;">return</span> R.success(<span style="color: black;">"数据导入成功,因数据量比<span style="color: black;">很强</span>,已转为异步导入"</span>);
}
<span style="color: black;">// 省略部分代码</span>
<span style="color: black;">// 实例数据解析监听器</span>
EasyExcelListener<ProjectInfoDTO> easyExcleListener = <span style="color: black;">new</span> EasyExcelListener(projectInfoService,ProjectInfoDTO<span style="color: black;">.<span style="color: black;">class</span>)</span>;
<span style="color: black;">// 文件读取/解析,并注册监听器</span>
EasyExcle.read(file.getInputStream(),ProjectInfoDTO<span style="color: black;">.<span style="color: black;">class</span>,<span style="color: black;">easyExcleListener</span>).<span style="color: black;">sheet</span>(1).<span style="color: black;">doRead</span>()</span>;
<span style="color: black;">// 获取错误数据</span>List<ExcelCheckErrDTO<ProjectInfoExcelDTO>> errList = easyExcleListener.getErrList();<span style="color: black;">// 获取解析成功到数据</span>
List<ProjectinfoExcelDTO> successList = easyExcleListener.getSuccessList();
<span style="color: black;">// <span style="color: black;">倘若</span>错误数据不为空,将错误数据写入到excel文件,并输出到浏览器</span>
<span style="color: black;">if</span> (errList.size() > <span style="color: black;">0</span> ){
<span style="color: black;">// 省略部分代码</span>
}
<span style="color: black;">// 将成功到数据,批量写入到数据库中</span>
<span style="color: black;">// 省略代码</span>
<span style="color: black;">// 省略其他代码</span>
}
}<h1 style="color: black; text-align: left; margin-bottom: 10px;">异步导入</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">异步导入操作,将思考几个问题:</span></p><span style="color: black;">导入文件存到什么<span style="color: black;">地区</span>?当一个同步请求结束之后,后续<span style="color: black;">咱们</span>想再次拿到该请求到数据,<span style="color: black;">咱们</span>应该<span style="color: black;">思虑</span>将文件放到某一个单独到<span style="color: black;">地区</span>,<span style="color: black;">供给</span><span style="color: black;">咱们</span>二次<span style="color: black;">运用</span>,<span style="color: black;">例如</span>:自己到文件服务器、oss 存储等,<span style="color: black;">这儿</span><span style="color: black;">咱们</span><span style="color: black;">运用</span>自己的文件服务器。</span><span style="color: black;">怎么异步执行?<span style="color: black;">咱们</span><span style="color: black;">能够</span><span style="color: black;">运用</span>新启用一个本地线程去执行<span style="color: black;">咱们</span>的操作,不影响当前请求主线程的操作,<span style="color: black;">亦</span>是<span style="color: black;">能够</span>的,<span style="color: black;">然则</span><span style="color: black;">思虑</span>到执行重试问题,<span style="color: black;">咱们</span>将<span style="color: black;">运用</span>(#xxl-job)分布式调度系统,进行调度执行任务。</span><span style="color: black;">客户<span style="color: black;">怎样</span>查看任务执行状态?<span style="color: black;">咱们</span>需要<span style="color: black;">供给</span>一个任务执行日志列表,让用户<span style="color: black;">能够</span>清晰的看到<span style="color: black;">这次</span>导出的任务<span style="color: black;">是不是</span>执行完成/<span style="color: black;">是不是</span>存在导入错误。</span><span style="color: black;">怎么将错误报告输出给到客户?<span style="color: black;">咱们</span>需要将导入到错误报告文件(excel)上传至文件服务器,<span style="color: black;">供给</span>用户二次或多次下载<span style="color: black;">运用</span>;<span style="color: black;">同期</span>,需要将文件信息<span style="color: black;">保留</span>至任务执行日志信息中,为用户<span style="color: black;">供给</span>下载入口。</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">定义通用的job handler 父类 AsyncTaskHandler ,所有需要<span style="color: black;">运用</span>xxl-job 发起异步任务和给xxl-job 发起回调,都需要继承AsyncTaskHandler ,并实现execute 抽象<span style="color: black;">办法</span>。</span></p><span style="color: black;">public</span> <span style="color: black;">abstract</span> <span style="color: black;">class</span>AsyncTaskHandler <T<span style="color: black;">extends</span> AsyncTaskPramsDTO> {
<span style="color: black;">/** xxl-job server 端<span style="color: black;">供给</span>的创建任务接口 uri **/</span>
<span style="color: black;">private</span> final <span style="color: black;">static</span> <span style="color: black;">String</span> JOB_ADMIN_URI = <span style="color: black;">"/outapi/asyn/"</span>;
<span style="color: black;">/** 与xxl-job server 通讯的加密密钥对 **/</span>
<span style="color: black;">@Setter</span>
<span style="color: black;">protected</span> <span style="color: black;">String</span> publicKey;
<span style="color: black;">/**
* xxl-job server 回调对<span style="color: black;">办法</span>
*/</span>
<span style="color: black;">public</span> <span style="color: black;">abstract</span> ReturnT<<span style="color: black;">String</span>> execute(<span style="color: black;">String</span> params);
<span style="color: black;">/**
* 向xxl-job 发起调度任务
*/</span>
<span style="color: black;">public</span> JobResponseDTO sendTask(T prams){
prams.setUser(<span style="color: black;">null</span>);
<span style="color: black;">// 省略部分代码,<span style="color: black;">关联</span>内容,请<span style="color: black;">查找</span>xxl-job server 端所<span style="color: black;">供给</span>的接口文档</span>
<span style="color: black;">// 将 params 中的 user 对象<span style="color: black;">保留</span>至redis 中,xxl-job 接口有长度限制</span>
}
<span style="color: black;">public</span> <span style="color: black;">abstract</span> RedisUtil getRedisUtil();
<span style="color: black;">public</span> <span style="color: black;">abstract</span> JobProperties getJobProperties();
<span style="color: black;">/** 回调<span style="color: black;">办法</span>名<span style="color: black;">叫作</span> **/</span>
<span style="color: black;">public</span> <span style="color: black;">abstract</span> <span style="color: black;">String</span> getHandlerName();
}
定义 AsyncTaskPramsDTO 异步参数实体
<span style="color: black;">@Data</span>
<span style="color: black;">// 省略其他注解</span>
<span style="color: black;">public</span> <span style="color: black;">class</span> AsyncTaskPramsDTO {
<span style="color: black;">private</span> <span style="color: black;">String</span> requestId;
}<h1 style="color: black; text-align: left; margin-bottom: 10px;">数据导出</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">数据导出功能常指,客户想将系统中的<span style="color: black;">关联</span>(<span style="color: black;">根据</span><span style="color: black;">查找</span><span style="color: black;">要求</span>筛选)数据<span style="color: black;">经过</span>excel形式<span style="color: black;">保留</span>到自己本地。在数据导出过程中,需要<span style="color: black;">经过</span>数据筛选<span style="color: black;">要求</span>将数据从系统数据库中筛选出来,<span style="color: black;">而后</span><span style="color: black;">经过</span><span style="color: black;">必定</span>格式(excel导出模版格式)写入到excel中,最后输出到客户端(浏览器)<span style="color: black;">供给</span>客户下载<span style="color: black;">保留</span>到本地。</span></p>
你的努力一定会被看见,相信自己,加油。 期待与你深入交流,共探知识的无穷魅力。 楼主发的这篇帖子,我觉得非常有道理。
页:
[1]