使用easyexcel将csv转为excel

news2024/11/18 14:55:53

一.背景

        供应商系统下载的csv文件不支持域控(主要是第三方wps服务不能对csv文件加密,但是可以对office系列产品进行权限访问的加密控制)。因此思路就改为现将csv文件转为excel文件,然后对excel文件进行加域控制。本文主要介绍如何将csv文件转为excel文件。

二.要求

  1.         Csv文件可能比较大,达到40-60M,需要控制内存使用率;
    1.         考虑接口的并发,需要进行接口的限流
  2. 三.方案

    1.         采用alibaba的easyexcel,降低内存占用率,根据压测结果,设置合理的接口限流参数(限流
    2. 本文不再介绍,可以使用java注解+redis+lua, 或者nginx限流等)
    3. 四.代码

    4. CsvController

  3. package com.xxx.xxx.controller;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.Future;
    
    import javax.annotation.Resource;
    import javax.servlet.http.HttpServletResponse;
    
    import com.xxx.xxx.common.utils.EasyExcelUtil;
    import com.xxx.xxx.common.utils.ObjectUtil;
    import com.xxx.xxx.service.ExcelAnalysisService;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.multipart.MultipartFile;
    
    /**
     * description:
     *
     * @author: lgq
     * @create: 2024-04-16 11:06
     */
    @Slf4j
    @RestController
    @RequestMapping("/csv")
    public class CsvController {
        @Resource
        private ExcelAnalysisService excelAnalysisService;
    
        /**
         * 读取传入的csv  文本的内容可以存入数据库
         *
         * @param file
         * @return
         */
        @PostMapping("/uploadCsvAndImportExcel")
        public void uploadCsvAndImportExcel(@RequestParam("file") MultipartFile file, HttpServletResponse response) {
            String[] splitName = file.getOriginalFilename().split(".csv");
            if (ObjectUtil.isEmpty(splitName) || ObjectUtil.isEmpty(splitName[0])) {
                return;
            }
            EasyExcelUtil.setResponseParam(response, splitName[0]);
            long startTime = System.currentTimeMillis();
            log.info("导出开始时间:{}", startTime);
    
            try {
                // 输出流可以为本地文件
    //          OutputStream outputStream = new FileOutputStream("D:\\templateExcel\\filename.xlsx");
                OutputStream outputStream = response.getOutputStream();
                InputStream inputStream = file.getInputStream();
                Future<String> future = excelAnalysisService.csv2Excel(inputStream, outputStream);
                future.get();
            } catch (IOException ioException) {
                log.error("csv转为excel出错!", ioException.getMessage());
                ioException.printStackTrace();
            } catch (InterruptedException interruptedException) {
                log.error("csv转为excel出错!", interruptedException.getMessage());
                interruptedException.printStackTrace();
            } catch (ExecutionException executionException) {
                log.error("csv转为excel出错!", executionException.getMessage());
                executionException.printStackTrace();
            }
            // 导出时间结束
            long endTime = System.currentTimeMillis();
            log.info("导出结束时间:{}", endTime + "ms");
            log.info("导出所用时间:{}", (endTime - startTime) / 1000 + "秒");
        }
    
    }
    

    EasyExcelGeneralCsvListener 

  4. package com.xxx.xxx.listener;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.Map;
    
    import com.alibaba.excel.ExcelWriter;
    import com.alibaba.excel.context.AnalysisContext;
    import com.alibaba.excel.event.AnalysisEventListener;
    import com.alibaba.excel.write.metadata.WriteSheet;
    import com.xxx.xxx.constants.ExcelConstants;
    
    /**
     * description:
     *
     * @author: lgq
     * @create: 2024-04-16 11:25
     */
    public class EasyExcelGeneralCsvListener extends AnalysisEventListener<Map<Integer, String>>  {
        /**
         * 用于存储读取的数据
         */
        private List<Map<Integer, String>> dataList = new ArrayList<>();
    
        private ExcelWriter excelWriter;
    
        private WriteSheet writeSheet;
    
        public EasyExcelGeneralCsvListener() {
        }
    
        public EasyExcelGeneralCsvListener(ExcelWriter excelWriter, WriteSheet writeSheet) {
            this.excelWriter = excelWriter;
            this.writeSheet = writeSheet;
        }
    
        @Override
        public void invoke(Map<Integer, String> data, AnalysisContext context) {
            // 数据add进入集合
            dataList.add(data);
            // size是否为2000条:这里其实就是分批.当数据等于2k的时候执行一次写入excel
            if (dataList.size() >= ExcelConstants.PER_WRITE_EXCEL_ROW_COUNT) {
                save2Excel();
                // 清理集合便于GC回收
                dataList.clear();
            }
        }
    
        @Override
        public void invokeHeadMap(Map<Integer, String> headers, AnalysisContext context) {
            List<List<String>> titles = new ArrayList<>();
            for (int i = 0; i < headers.size(); i++) {
                titles.add(Collections.singletonList(headers.get(i)));
            }
            this.writeSheet.setHead(titles);
        }
    
        /**
         * 保存数据到 excel
         */
        private void save2Excel() {
            if (dataList.size() > 0) {
                List<List<String>> consumerDataList = new ArrayList<>();
                dataList.stream().forEach( e ->
                        {
                            List<String> objects = new ArrayList<>();
                            for (int i = 0; i < e.size(); i++) {
                                objects.add(e.get(i));
                            }
                            consumerDataList.add(objects);
                        }
    
                );
                this.excelWriter.write(consumerDataList, writeSheet);
            }
        }
    
        /**
         * Excel 中所有数据解析完毕会调用此方法
         */
        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            save2Excel();
            dataList.clear();
        }
    
    }
    

    VisiableThreadPoolTaskExecutor

  5. package com.xxx.xxx.task;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.Future;
    import java.util.concurrent.ThreadPoolExecutor;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    import org.springframework.util.concurrent.ListenableFuture;
    
    /**
     * description:VisiableThreadPoolTaskExecutor
     *
     * @author: lgq
     * @create: 2024-04-17 10:52
     */
    @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);
        }
    }
    
    
    ExcelAnalysisService
  6. package com.xxx.xxx.service;
    
    import java.io.OutputStream;
    import java.io.InputStream;
    import java.util.concurrent.Future;
    
    /**
     * description:excel文档分析处理类
     *
     * @author: lgq
     * @create: 2024-04-17 11:42
     */
    public interface ExcelAnalysisService {
        /**
         * csv文档转为excel文档
         */
        Future<String> csv2Excel(InputStream inputStream, OutputStream outputStream);
    }
    

    ExcelAnalysisServiceImpl

  7. package com.xxx.xxx.service.impl;
    
    import java.io.OutputStream;
    import java.nio.charset.Charset;
    
    import com.alibaba.excel.EasyExcel;
    import com.alibaba.excel.ExcelWriter;
    import com.alibaba.excel.support.ExcelTypeEnum;
    import com.alibaba.excel.write.metadata.WriteSheet;
    import com.xxx.xxx.listener.EasyExcelGeneralCsvListener;
    import com.xxx.xxx.service.ExcelAnalysisService;
    
    import lombok.extern.slf4j.Slf4j;
    import java.io.InputStream;
    import java.util.concurrent.Future;
    
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.scheduling.annotation.AsyncResult;
    import org.springframework.stereotype.Service;
    
    /**
     * description:ExcelAnalysisService实现类
     *
     * @author: lgq
     * @create: 2024-04-17 14:53
     */
    @Service
    @Slf4j
    public class ExcelAnalysisServiceImpl implements ExcelAnalysisService {
    
        @Async("asyncExcelAnalysisServiceExecutor")
        @Override
        public Future<String> csv2Excel(InputStream inputStream, OutputStream outputStream) {
            try {
                ExcelWriter writer = EasyExcel.write(outputStream).excelType(ExcelTypeEnum.XLSX).build();
    
                EasyExcel.read(inputStream, new EasyExcelGeneralCsvListener(writer, new WriteSheet()))
                        .excelType(ExcelTypeEnum.CSV)
                        .charset(Charset.forName("UTF-8"))
                        .sheet()
                        .doRead();
    
                writer.finish();
                outputStream.flush();
            } catch (Exception e) {
                log.error("csv转为excel出错!", e.getMessage());
                e.printStackTrace();
            } finally {
                if (outputStream != null) {
                    try {
                        outputStream.close();
                    } catch (Exception e) {
                        log.error("outputStream.close() -> csv转为excel出错!", e.getMessage());
                        e.printStackTrace();
                    }
                }
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (Exception e) {
                        log.error("inputStream.close() -> csv转为excel出错!", e.getMessage());
                        e.printStackTrace();
                    }
                }
            }
            return new AsyncResult<>("task complete!");
        }
    }
    

    ExecutorConfig

  8. package com.xxx.xxx.config;
    
    import java.util.concurrent.Executor;
    import java.util.concurrent.ThreadPoolExecutor;
    
    
    import com.xxx.xxx.task.VisiableThreadPoolTaskExecutor;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.scheduling.annotation.EnableAsync;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    
    /**
     * description:线程池配置类
     *
     * @author: lgq
     * @create: 2024-04-17 10:28
     */
    @Configuration
    @Slf4j
    @EnableAsync
    public class ExecutorConfig {
        private static int corePoolSize = Runtime.getRuntime().availableProcessors() + 1;
        private static int maxPoolSize = Runtime.getRuntime().availableProcessors() + 1;
        private static int queueCapacity = 100;
        private static final String namePrefix = "ExcelAnalysis";
    
        @Bean(name = "asyncExcelAnalysisServiceExecutor")
        public Executor asyncExcelServiceExecutor() {
            log.info("start asyncExcelAnalysisServiceExecutor----------------");
            //ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            //使用可视化运行状态的线程池
            ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
            //配置核心线程数
            executor.setCorePoolSize(corePoolSize);
            //配置最大线程数
            executor.setMaxPoolSize(maxPoolSize);
            //配置队列大小
            executor.setQueueCapacity(queueCapacity);
            //配置线程池中的线程的名称前缀
            executor.setThreadNamePrefix(namePrefix);
    
            // rejection-policy:当pool已经达到max size的时候,如何处理新任务
            // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    
            //执行初始化
            executor.initialize();
            log.info("end asyncExcelAnalysisServiceExecutor------------");
            return executor;
        }
    
    }
    

    ExcelConstants

  9. package com.xxx.xxx.constants;
    
    /**
     * description:线程池配置类
     *
     * @author: lgq
     * @create: 2024-04-17 10:28
     */
    public class ExcelConstants {
        public static final Integer PER_SHEET_ROW_COUNT = 100*10000;
        public static final Integer PER_WRITE_ROW_COUNT = 20*10000;
        public static final Integer PER_WRITE_EXCEL_ROW_COUNT = 2 * 1000;
        public static final Integer GENERAL_ONCE_SAVE_TO_DB_ROWS_JDBC = 10*10000;
        public static final Integer GENERAL_ONCE_SAVE_TO_DB_ROWS_MYBATIS = 5*10000;
    }
    

    配置文件

  10. spring:
      servlet:
        multipart:
          enabled: true
          max-file-size: 100MB # 单个文件的最大值
          max-request-size: 100MB # 上传文件总的最大值

    pom依赖

  11.         <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>easyexcel</artifactId>
                <version>3.3.2</version>
            </dependency>
  12. 五.压测

  13. jvm参数(本地电脑,性能较差)
  14. -Xms2g -Xmx2g
  15. 导出日志

性能监控

压测结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1614215.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL学习笔记5——函数和索引

函数和索引 一、函数1、数学函数2、字符串函数3、条件判断函数 二、索引1、索引是什么2、单字段索引3、组合索引4、总结 一、函数 MySQL提供了很多功能强大&#xff0c;而且使用起来非常方便的函数&#xff0c;包括数学函数、字符串处理函数和条件判断函数等。 1、数学函数 …

Flink面试(1)

1.Flink 的并行度的怎么设置的&#xff1f; Flink设置并行度的几种方式 1.代码中设置setParallelism() 全局设置&#xff1a; 1 env.setParallelism(3);  算子设置&#xff08;部分设置&#xff09;&#xff1a; 1 sum(1).setParallelism(3) 2.客户端CLI设置&#xff0…

shell简单联系项目

文章目录 推荐一个vscode上的好用的神奇如何使用的方式连接主机的方式配置新主机配置信息启动连接的方式 联系shell 命令的方式读取文件信息设置本地环境变量的方式获取随机数的方式简单案例信息 推荐一个vscode上的好用的神奇 如何使用的方式 连接主机的方式 配置新主机 配置…

运维小技能:nacos部署(外接mysql)

文章目录 I 安装nacos(m1版本)1.1 镜像启动1.2 查看docker容器日志1.3 开启鉴权II 外接mysql的docker部署方式2.1 复制mysql-schema.sql2.2 导入mysql-schema.sqlIII 配置远程用户3.1 创建数据库远程用户3.2 查看远程用户是否有密码I 安装nacos(m1版本) docker search nacos:查…

集成触发器(数电笔记)

同步触发器&#xff1a; 主从触发器&#xff1a; 边沿触发器&#xff1a;

【团体程序设计天梯赛】L2-052 吉利矩阵

思路&#xff1a; 直接回溯枚举每一个位置填的数&#xff0c;二维肯定是不方便的&#xff0c;我们转成一维&#xff0c;下标x从0到n*n-1。二维数组下标从0到n-1&#xff0c;在一维中下标为x的点在二维中对应行是x/n&#xff0c;列是x%n。 每个数最小能填的是0&#xff0c;最大…

一套在线画图工具(突突图 Procviz)

突突图(Procviz)是一款面向跨平台作图平台。支持流程图、思维导图、框架图、组织架构图、ER图、网络拓扑图等。实现了多团体同时协作&#xff0c;实时同步&#xff0c;解决跨地域合作作图的问题。平台提供了丰富的模板和素材库&#xff0c;轻松完成作图&#xff0c;效率翻倍。 …

imx6ull设备树驱动--pinctl、ioctl

添加pinctl节点 进入arch/arm/boot/dts目录下dts文件 在iomuxc下添加pinctlled节点 将 GPIO1_IO03 这个 PIN 复用为 GPIO1_IO03&#xff0c;电气属性&#xff08;配置GPIO一些列寄存器&#xff09;值为 0X10B0 添加led设备节点 与上一节一样&#xff0c;在 / 下面添加设备节…

《AIGC辅助数据分析与挖掘》AIGC助力数据可视化:Excel图形化思维与实施技巧

01 前言 在当今的数据分析中&#xff0c;数据可视化扮演着至关重要的角色。作为一款常用工具&#xff0c;Excel提供了丰富的图形展示功能。利用AIGC&#xff0c;我们可以快速选择合适的图形类型&#xff0c;并进行专业的配置&#xff0c;从而使得数据展示更加吸引人且具有更好…

钟薛高创始人称卖红薯也把债还上:网友,您可千万别……

网红雪糕品牌钟薛高&#xff0c;是真的网红属性强到让所有消费品牌羡慕。 纵使跌落神坛、纵使站在「破产」边缘&#xff0c;依然话题感满满&#xff0c;隔段时间&#xff0c;总能上一个热搜。 比如欠薪上热搜、产品降价上热搜、甚至官网微博微信停更&#xff0c;也得上个热搜&…

VSCode插件开发学习

一、环境准备 0、参考文档&#xff1a;VS Code插件创作中文开发文档 1、大于18版本的nodejs 2、安装Yeoman和VS Code Extension Generator&#xff1a; npm install -g yo generator-code 3、生成脚手架 yo code 选择内容&#xff1a; ? What type of extension do yo…

【1524】java投票管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 java 投票管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0&…

ESG热潮背后

近年来&#xff0c;ESG投资在全球范围内迅速发展&#xff0c;已经成为行业新风口。越来越多的投资者和企业开始关注ESG因素&#xff0c;并将其纳入投资决策和公司运营中。 ESG指环境&#xff08;Environmental&#xff09;、社会&#xff08;Social&#xff09;、公司治理&…

揭秘!综合布线可视化管理软件如何助力集成商实现价值飞跃?

一、弱电集成商发展现状 近期小编通过与多家做弱电集成的朋友交流探讨了解到目前弱电集成商发展如同2024年国内大部分企业一样举步维艰&#xff0c;当然也有个别企业做的项目优质并且利润可观&#xff0c;但是整体不多&#xff0c;总结原因主要有以下几点&#xff1a; 工程项目…

Robbins-Monro(RM)算法【随机近似】

强化学习笔记 主要基于b站西湖大学赵世钰老师的【强化学习的数学原理】课程&#xff0c;个人觉得赵老师的课件深入浅出&#xff0c;很适合入门. 第一章 强化学习基本概念 第二章 贝尔曼方程 第三章 贝尔曼最优方程 第四章 值迭代和策略迭代 第五章 强化学习实践—GridWorld 第…

C语言枚举类型详解

下午好诶&#xff0c;今天小眼神给大家带来一篇C语言枚举类型详解的文章~ 目录 一、枚举类型的声明 二、枚举类型的优点 三、枚举类型的使用 一、枚举类型的声明 枚举顾名思义就是 一 一 列 举 。 比如&#xff1a; 一周从周一到周日共有七天&#xff0c;可以一一列举。 性…

ARM汇编伪指令AREA

ARM伪指令AREA用于定义一个代码段或数据段。其基本的语法格式如下&#xff1a; AREA 段名 属性1,属性2, ... 其中&#xff1a; 段名&#xff1a;是你为代码段或数据段指定的名称。如果段名以数字开头&#xff0c;则该段名需要用“│”括起来&#xff0c;如│1_test│。段名可…

文档在线预览,可以私有化局域网在服务器部署组件来实现在线预览

去官网查看 几行代码即可运行 https://kkfileview.keking.cn/zh-cn/index.html

引用静态方法

import java.util.Arrays; import java.util.Comparator;public class demo1 {//引用public static void main(String[] args) {Integer []arr{1,2,4,3,8,6};//匿名内部类Arrays.sort(arr, new Comparator<Integer>() {Overridepublic int compare(Integer o1, Integer o…

深度神经网络(DNN)

通过5个条件判定一件事情是否会发生&#xff0c;5个条件对这件事情是否发生的影响力不同&#xff0c;计算每个条件对这件事情发生的影响力多大&#xff0c;写一个深度神经网络&#xff08;DNN&#xff09;模型程序,最后打印5个条件分别的影响力。 示例 在深度神经网络&#xf…