【java】Excelの非同期ダウンロードを実現する

必要:

ユーザーは [ファイルの生成] ボタンをクリックしてバックエンドでファイルを生成し、後でダウンロードするために [ダウンロード] ボタンをクリックするようにユーザーに求めます。
バックエンドがファイルを生成してサーバーに保存しますが、ダウンロードボタンをクリックすると、ダウンロード直後にサーバー上のファイルが削除されます。

1. @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);
    }
    
}    

2. カスタム スレッド プール:

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);
    }
}

3. 非同期処理のメソッドに @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文件结束!");
    }

4 番目に、非同期メソッドを呼び出します。

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();
}

おすすめ

転載: blog.csdn.net/m0_46459413/article/details/130523767