【java】实现异步下载Excel
用户点击【生成文件】按钮进行文件的后端生成,并提示用户稍后点击【下载】按钮进行下载。后端生成文件后存储到服务器中,点击下载按钮时文件下载完毕后随即立刻删除服务器中的文件。
·
需求:
用户点击【生成文件】按钮进行文件的后端生成,并提示用户稍后点击【下载】按钮进行下载。
后端生成文件后存储到服务器中,点击下载按钮时文件下载完毕后随即立刻删除服务器中的文件。
一、在启动类中加入@EnableAsync 注解启用异步
@EnableAsync
@EnableCaching
@EnableEurekaClient
@EnableTransactionManagement
@EnableFeignClients(basePackages = {"com.ylz.bjyf"})
@SpringBootApplication(exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class})
@EnableScheduling
public class XxxxApplication {
public static void main(String[] args) {
SpringApplication.run(XxxxApplication.class, args);
}
}
二、自定义线程池:
package com.ylz.bjyf.framework.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.lang.reflect.Method;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
@EnableAsync
@Slf4j
public class ExecutorConfig implements AsyncConfigurer {
public static Integer smsAsynSize = Runtime.getRuntime().availableProcessors();
@Autowired
private TaskThreadPoolConfig threadPoolConfig;
@Bean("asyncServiceExecutor")
public ThreadPoolTaskExecutor asyncServiceExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = threadPoolConfig.getIsPrint().equals("NO") ? new ThreadPoolTaskExecutor() : new VisiableThreadPoolTaskExecutor();
log.info("异步核心线程数:{}", smsAsynSize);
//配置核心线程数
threadPoolTaskExecutor.setCorePoolSize(smsAsynSize);
//配置最大线程数
threadPoolTaskExecutor.setMaxPoolSize(smsAsynSize * 2);
//配置队列大小
threadPoolTaskExecutor.setQueueCapacity(smsAsynSize * 3);
// 空闲线程存活时间
threadPoolTaskExecutor.setKeepAliveSeconds(threadPoolConfig.getKeepAliveSeconds());
//配置线程池中的线程的名称前缀
threadPoolTaskExecutor.setThreadNamePrefix("gjn-asyncServiceExecutor-thread-");
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//执行初始化
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new MyAsyncExceptionHandler();
}
class MyAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
//手动处理捕获的异常
@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
log.error("线程执行异常", throwable);
log.error("ExceptionMessage:{}", throwable.getMessage());
log.error("MethodName:{}", method.getName());
for (Object param : obj) {
log.error("Parameter:{}", param);
}
}
}
}
package com.ylz.bjyf.framework.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "task.pool")
public class TaskThreadPoolConfig {
private int corePoolSize = 35;
private int maxPoolSize = 60;
private int keepAliveSeconds = 300;
private int queueCapacity = 50;
// 等待超时时间
private long awaitTimeout = 5000;
// 是否打印线程池运行状态信息
private String isPrint = "NO";
}
package com.ylz.bjyf.framework.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.concurrent.ListenableFuture;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
@Slf4j
public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
private void showThreadPoolInfo(String prefix) {
ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();
if (null == threadPoolExecutor) {
return;
}
log.info("线程池【{}】的运行状况: 【{}】, taskCount 【{}】, completedTaskCount 【{}】, activeCount 【{}】, queueSize 【{}】",
this.getThreadNamePrefix(),
prefix,
threadPoolExecutor.getTaskCount(),
threadPoolExecutor.getCompletedTaskCount(),
threadPoolExecutor.getActiveCount(),
threadPoolExecutor.getQueue().size());
}
@Override
public void execute(Runnable task) {
showThreadPoolInfo("1.do execute");
super.execute(task);
}
@Override
public void execute(Runnable task, long startTimeout) {
showThreadPoolInfo("2.do execute");
super.execute(task, startTimeout);
}
@Override
public Future<?> submit(Runnable task) {
showThreadPoolInfo("1.do submit");
return super.submit(task);
}
@Override
public <T> Future<T> submit(Callable<T> task) {
showThreadPoolInfo("2.do submit");
return super.submit(task);
}
@Override
public ListenableFuture<?> submitListenable(Runnable task) {
showThreadPoolInfo("1.do submitListenable");
return super.submitListenable(task);
}
@Override
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
showThreadPoolInfo("2.do submitListenable");
return super.submitListenable(task);
}
}
三、在异步处理的方法上添加注解 @Async
@Async("asyncServiceExecutor")
public void exportJcsjlrForMongo(List<Jcsjlr> list ,String checkFlag) {
//查询要导出的数据
List<JcsjlrDTO> jcsjlrList = new ArrayList<>();
log.info("异步生成Excel文件开始!");
for (Jcsjlr jcsjlr : list) {
JcsjlrDTO jcsjlrDTO = new JcsjlrDTO();
BeanUtils.copyProperties(jcsjlr, jcsjlrDTO);
jcsjlrList.add(jcsjlrDTO);
}
log.info("导出数据量:"+jcsjlrList.size());
//生成Excel文件,将文件放到服务器中
Date day = new Date();
SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
System.out.println(df.format(day));
String dateTime = df.format(day);
//要导出的excel名称
String fileName = "";
if("checkList".equals(checkFlag)){
fileName = "checkList.xlsx";
} else if("showErrorList".equals(checkFlag)){
fileName = "showErrorList.xlsx";
} else {
fileName = "other.xlsx";
}
// 模板地址
String templateFileName = "template" + File.separator + "qyjcsjTemplate.xlsx";
//文件生成地址 本地
String filePath = "E:\\files"+ File.separator + fileName;
//导出模板
ExcelWriter excelWriter = null;
try {
InputStream inputStream = new ClassPathResource(templateFileName).getInputStream();
excelWriter = EasyExcel.write(filePath).withTemplate
(inputStream).build();
} catch (IOException e) {
log.error("导出文件异常"+e);
}
//读取Excel
WriteSheet writeSheet = EasyExcel.writerSheet().build();
//是否新增行
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
excelWriter.fill(jcsjlrList, fillConfig, writeSheet);
Map<String, Object> map = new HashMap<String, Object>();
map.put("year",dateTime.substring(0,4));
map.put("month",dateTime.substring(4,6));
excelWriter.fill(map, writeSheet);
// 关闭流
excelWriter.finish();
log.info("异步生成Excel文件结束!");
}
四、调用异步方法
exportAsync方法是异步生成文件并存储到服务器中,
downLoad方法是下载文件并删除服务器中存储的文件。
/**
* 信息的导出,异步版
* @param exportDTO
* @return
*/
@RequestMapping("/exportAsync")
public ResultVO exportAsync(@RequestBody ExportDTO exportDTO){
//查询要导出的数据
List<JcsjlrDTO> jcsjlrList = new ArrayList<>();
List<Jcsjlr> list = new ArrayList<>();
if("1".equals(exportDTO.getSfqx()) && StringUtils.isNotBlank(exportDTO.getMethodsName())){
//按方法名称,分类调用,获取全部的统一社会信用代码列表
JcsjlrQueryDTO queryDTO = new JcsjlrQueryDTO();
BeanUtils.copyProperties(exportDTO,queryDTO);
//分情况调用异步生成文件接口
if("checkList".equals(exportDTO.getMethodsName())){
list = jcsjlrBPO.checkAll(queryDTO); //业务数据查询
exportExcelBPO.exportJcsjlrForMongo(list,"checkList");//调用异步接口
return new ResultVO().addData("data","正在生成中,请稍后点击下载按钮进行下载!");
} else if ("showErrorList".equals(exportDTO.getMethodsName())) {
//业务数据查询
List<JcsjlrDTO> jcsjlrDTOS = jcsjlrBPO.showErrorList(queryDTO);
//转换类型
for (JcsjlrDTO jcsjlrDTO : jcsjlrDTOS) {
Jcsjlr jcsjlr = new Jcsjlr();
BeanUtils.copyProperties(jcsjlrDTO, jcsjlr);
list.add(jcsjlr);
}
exportExcelBPO.exportJcsjlrForMongo(list,"showErrorList");//调用异步接口
return new ResultVO().addData("data","正在生成中,请稍后点击下载按钮进行下载!");
}
} else {
exportExcelBPO.exportJcsjlrForMongo(list,"other");//调用异步接口
return new ResultVO().addData("data","正在生成中,请稍后点击下载按钮进行下载!");
}
return new ResultVO("生成Excel异常,请联系管理员进行排查!");
}
/**
* 下载Excel
* @param checkFlag
*/
@RequestMapping("/downLoad")
public ResultVO downLoad(String checkFlag, HttpServletResponse response){
//获取文件,返给前端
String path = "E:\\files"+ File.separator;
String fileName = checkFlag + ".xlsx";
//获取文件
File file = new File(path + fileName);
if(!file.exists()){
return new ResultVO("您已下载文件,若需再次下载,请点击导出按钮重新生成!");
}
try {
//开始时间
long old = System.currentTimeMillis();
//添加文件导出格式限制
response.setContentType("application/vnd.ms-excel;charset=utf-8");
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream());
byte[] buff = new byte[2048];
int bytesRead;
while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
bos.write(buff, 0, bytesRead);
}
//结束时间
long now = System.currentTimeMillis();
System.out.println("文件下载结束!共耗时:" + ((now - old) / 1000.0) + "秒");
bis.close();
bos.close();
} catch (Exception e) {
log.info("生成文件流异常");
e.printStackTrace();
}finally {
//下载完毕后删除文件
file.delete();
}
return new ResultVO();
}
更多推荐
已为社区贡献2条内容
所有评论(0)