栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

异步线程池+多线程实现EXCEL填充,打包(zip)及上传

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

异步线程池+多线程实现EXCEL填充,打包(zip)及上传

需求背景:

1、通过筛选条件过滤数据(多线程主要解决这一步查询慢的问题)

2、数据填充EXCEL

3、多EXCEL打zip包上传到文件服务器

4、先返回下载信息,再同步导出EXCEL报表数据

主要逻辑和方法:

注册异步线程池

     
    @Bean("asyncThreadPool")
    public ThreadPoolTaskExecutor asyncThreadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(20);
        executor.setQueueCapacity(100);
        executor.setKeepAliveSeconds(30);
        executor.setThreadNamePrefix("async-Thread");
        executor.setWaitForTasksToCompleteonShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.setTaskDecorator(new ContextCopyingDecorator());
        executor.initialize();
        return executor;
    }

service实现方法

        
    @Override
    @Async("asyncThreadPool")
    public void exportProjectDetailByUser(DataCenterDetailByUserQueryRequest request, String fileName, String minaShareDirPath, String templatePathTag, String id) {
        //生成EXCEL的保存路径:文件路径+yyyyMMdd+UUID
        String excelFilePath = DataCenterReportUtil.getExcelDownloadPath(minaShareDirPath);
        //String local = "F:\home\excel";
        //String excelFilePath = local + File.separator + DateUtil.format(new Date(), "yyyyMMdd") + File.separator + UUIDGenerator.getUUID() + File.separator;
        try {
            //创建文件夹
            DataCenterReportUtil.createFile(excelFilePath);
            //填充模板数据并生成zip包
            this.exportTemplateDataThread(request, excelFilePath, templatePathTag, fileName);
            //zip包路径:上传文件服务器使用
            String zipFilePath = excelFilePath + fileName + ".zip";
            //上传后返回下载url
            String downloadUrl = fileService.uploadFileWithForest(zipFilePath);
            //更新表
            dataCenterService.updateDataCenter(id, downloadUrl, "报表导出成功");
        } catch (Exception e) {
            //更新表
            dataCenterService.updateDataCenter(id, null, "报表导出失败");
        } finally {
            //删除excel及zip
            DataCenterReportUtil.deleteExcelFile(new File(excelFilePath));
        }
    }

文件操作util类

主要是创建文件夹,获取文件地址,删除文件夹及文件方法等。

public class DataCenterReportUtil {

    private static final Logger logger = LoggerFactory.getLogger(DataCenterReportUtil.class);

    
    public static final int PAGE_INTERVAL_SIZE = 2000;
    
    public static final int EXCEL_INTERVAL_SIZE = 10000;

    
    public static String getTemplatePath(String template) {
        return Objects.requireNonNull(DataCenterReportUtil.class.getResource(template)).getPath();
    }

    
    public static void createFile(String path) {
        File file = new File(path);
        //判断文件是否存在;
        if (!file.exists()) {
            //创建文件;
            boolean bol = file.mkdirs();
            if (bol) {
                logger.info("路径创建创建成功:{}", path);
            } else {
                logger.info("路径创建失败:{}", path);
            }
        } else {
            logger.info("文件已经存在:{}", path);
        }
    }

    
    public static boolean deleteExcelFile(File file) {
        String[] files = null;
        if (file != null) {
            files = file.list();
        }
        if (file.isDirectory()) {
            for (int i = 0; i < Objects.requireNonNull(files).length; i++) {
                boolean bol = deleteExcelFile(new File(file, files[i]));
                if (bol) {
                    logger.info("删除文件 {} 成功", files[i]);
                } else {
                    logger.info("删除文件 {} 失败", files[i]);
                }
            }
        }
        return file.delete();
    }

    
    public static String getExcelDownloadPath(String minaShareDirPath) {
        return minaShareDirPath + File.separator + DateUtil.format(new Date(), "yyyyMMdd") + File.separator + UUIDGenerator.getUUID() + File.separator;
    }

主要方法解析(线程池创建查询数据、填充模板、生成zip)

EXCEL的填充使用的是alibaba的EasyExcel,EasyExcel的使用方法可以参考链接:EasyExcel · 语雀

1、主方法

    //注入线程池
    @Autowired
    @Qualifier("asyncThreadPool")
    private ThreadPoolTaskExecutor asyncThreadPoolTaskExecutor;


    private void exportTemplateDataThread(DataCenterDetailByUserQueryRequest request, String excelFilePath, String templatePathTag, String fileName) {
        try {
            Long total = dataCenterService.detailByUserCount(request);
            if (total == null || 0 >= total) {
                this.richExcel(new ArrayList<>(), excelFilePath, fileName, templatePathTag);
            } else {
                //所有循环的次数
                int foreachSize = total.intValue() % DataCenterReportUtil.PAGE_INTERVAL_SIZE == 0 ? total.intValue() / DataCenterReportUtil.PAGE_INTERVAL_SIZE : total.intValue() / DataCenterReportUtil.PAGE_INTERVAL_SIZE + 1;
                //分页查询结果
                List list = Collections.synchronizedList(new ArrayList<>());
                //定义添加线程的集合
                List futureList = new ArrayList<>();
                //创建单个线程
                CompletableFuture future;
                //循环开始线程(还是使用线程池)
                for (int i = 1; i <= foreachSize; i++) {
                    DataCenterDetailByUserQueryRequest query = new DataCenterDetailByUserQueryRequest();
                    BeanUtils.copyProperties(request, query);
                    query.setPageNo(i);
                    query.setPageSize(DataCenterReportUtil.PAGE_INTERVAL_SIZE);
                    future = CompletableFuture.runAsync(() -> {
                        Page page = dataCenterService.detailByUser(query);
                        if (CollectionUtils.isNotEmpty(page.getRows())) {
                            list.addAll(page.getRows());
                        }
                    }, asyncThreadPoolTaskExecutor);
                    futureList.add(future);
                }
                //所有任务都执行完成
                CompletableFuture.allOf(futureList.toArray(new CompletableFuture[futureList.size()])).join();
                //把数据切分,每10000条插入一个excel
                List> splitList = ListUtil.split(list, DataCenterReportUtil.EXCEL_INTERVAL_SIZE);
                Map> dataMap = new HashMap<>(splitList.size());
                for (int i = 0; i < splitList.size(); i++) {
                    //设置EXCEL文件名
                    String key = fileName;
                    if (i >= 1) {
                        key = fileName + "(" + i + ")";
                    }
                    dataMap.put(key, splitList.get(i));
                }
                for (Map.Entry> entry : dataMap.entrySet()) {
                    //填充EXCEL
                    this.richExcel(entry.getValue(), excelFilePath, entry.getKey(), templatePathTag);
                }
            }
            //excel打包生成zip
            this.zip(excelFilePath, fileName);
        } catch (IOException e) {
            e.printStackTrace();
            log.info(e.toString());
        }
    }

2、填充excel

    private void richExcel(List list, String excelFilePath, String excelName, String templatePathTag) throws IOException {
        //excel模板地址
        String templatePath = DataCenterReportUtil.getTemplatePath(templatePathTag);
        //String templatePath = "F:\home\dataCenterDetailByUserTemplate.xlsx";
        String excel = excelName + ".xlsx";
        //生成的excel保存地址(全路径)
        String downloadFile = excelFilePath + excel;
        EasyExcel.write(downloadFile)
                .withTemplate(templatePath)
                .sheet()
                .doFill(list);
    }

3、文件流的方式打zip包

    
    private void zip(String excelFilePath, String zipName) throws IOException {
        File[] files = new File(excelFilePath).listFiles();
        File file = new File(excelFilePath + zipName + ".zip");
        ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
        FileInputStream fileInputStream;
        byte[] buf = new byte[1024];
        int len;
        if (files != null && files.length > 0) {
            for (File excelFile : files) {
                String fileName = excelFile.getName();
                //EXCEL文件流
                fileInputStream = new FileInputStream(excelFile);
                //放入压缩zip包中;
                zipOutputStream.putNextEntry(new ZipEntry(fileName));
                //读取文件;
                while ((len = fileInputStream.read(buf)) > 0) {
                    zipOutputStream.write(buf, 0, len);
                }
                //关闭;
                zipOutputStream.closeEntry();
                fileInputStream.close();
            }
        }
        //一定要关闭,如果不关闭无法操作zip
        zipOutputStream.close();
    }

上传到文件服务器

上传使用的是forest包

引用:


    com.dtflys.forest
    forest-spring-boot-starter
    1.5.13
    
    public interface ForestUploadClient {

    @Post("{0}")
    String upload(String url, @DataFile("Filedata") String filePath, onProgress onProgress);
    }
    @Autowired
    private ForestUploadClient uploadClient;    

    @Override
    public String uploadFileWithForest(String filePath) {
        try {
            //文件服务器上传地址
            String uploadUrl = this.getAllUploadUrl();
            //实现方法(上传)
            String response = uploadClient.upload(uploadUrl, filePath, progress -> {
                System.out.println("progress: " + Math.round(progress.getRate() * 100) + "%");  // 已上传百分比
                if (progress.isDone()) {   // 是否上传完成
                    System.out.println("--------   Upload Completed!   --------");
                }
            });
            document document = documentHelper.parseText(response);
            Element rootElement = document.getRootElement();
            String signedUrl = rootElement.element("signedUrl").getStringValue();
            return signedUrl;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }

到此完成。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/666739.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号