使用easyexcel将csv转为excel

一.背景

        供应商系统下载的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. 导出日志

性能监控

压测结果

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于IIoT的设备预测性维护设计

基于IIoT的设备预测性维护设计 一、引言 在工业物联网&#xff08;IIoT&#xff09;的背景下&#xff0c;设备预测性维护成为了一种关键的战略&#xff0c;能够帮助企业提前发现并解决设备故障&#xff0c;从而提高生产效率、减少停机时间&#xff0c;并降低总体维护成本。为了…

理解JMM

JMM 对volatile的理解 volatile 是java虚拟机提供轻量级的同步机制 1、保证可见性 2、不保证原子性 3、禁止指令重排 那么可见性与JMM相关 什么是JMM Java内存模型&#xff0c;不存在的东西&#xff0c;是一个概念&#xff0c;是一个约定 线程加锁前&#xff0c;必须读取…

【002_音频开发_基础篇_Linux音频架构简介】

002_音频开发_基础篇_Linux音频架构简介 文章目录 002_音频开发_基础篇_Linux音频架构简介创作背景Linux 音频架构ALSA 简介ASoC 驱动硬件架构软件架构MachinePlatformCodec ASoC 驱动 PCMALSA设备文件结构 ALSA 使用常用概念alsa-libALSA Open 流程ALSA Write 流程2种写入方法…

基础SQL DDL语句

MySQL的DDL&#xff08;Data Definition Language&#xff09;语句用于定义或修改数据库结构。 DDL数据库操作 查看所有的数据库 show databases; 红色圈起来的是系统数据库&#xff0c;是系统自带的 mysql&#xff1a;包含存储MySQL服务器运行时所需信息的表。这包括数据字典…

如何利用pg_dump和pg_restore迁移从一个PostgreSQL服务器到另一个服务器,同时保持一致性与高效性?

文章目录 解决方案1. 使用pg_dump导出数据2. 将导出的数据复制到目标服务器3. 使用pg_restore导入数据保持一致性与高效性的策略一致性高效性 示例代码导出数据复制数据到目标服务器在目标服务器上解压并导入数据 PostgreSQL数据库的迁移是一个常见的任务&#xff0c;特别是在升…

用Vue全家桶手工搓了一个高仿抖音源码 全开源

用Vue全家桶手工搓了一个高仿抖音&#xff0c;全开源 PC浏览器请用手机模式访问。先按F12调出控制台&#xff0c;再按CtrlShiftM切换到手机模式&#xff0c;手机请用Via浏览器或者Chrome浏览器预览。其他浏览器会强制将视频全屏&#xff0c;导致样式都失效。 运行项目&#x…

【C++】STL:vector常用接口的使用和模拟实现

Hello everybody!这篇文章主要给大家讲讲vector常用接口的模拟实现&#xff0c;STL库中的实现一层套着一层&#xff0c;十分复杂&#xff0c;目前阶段还不适合看源代码。而模拟实现可以让我们从底层上了解这些接口的原理从而更好的使用这些接口。另外我还会讲一些在vector使用过…

【第34天】SQL进阶-SQL高级技巧-Window Funtion(SQL 小虚竹)

回城传送–》《100天精通MYSQL从入门到就业》 文章目录 零、前言一、练习题目二、SQL思路初始化数据什么是Window Funtion窗口函数的分类语法结构第一种写法&#xff1a;第二种写法&#xff1a; 实战体验序号函数&#xff1a;row_number()序号函数&#xff1a;rank()序号函数&…

【树莓派Linux内核开发】入门实操篇(虚拟机Ubuntu环境搭建+内核源码获取与配置+内核交叉编译+内核镜像挂载)

【树莓派Linux内核开发】入门实操篇&#xff08;虚拟机Ubuntu环境搭建内核源码获取与配置内核交叉编译内核镜像挂载&#xff09; 文章目录 【树莓派Linux内核开发】入门实操篇&#xff08;虚拟机Ubuntu环境搭建内核源码获取与配置内核交叉编译内核镜像挂载&#xff09;一、搭建…

什么是0-day漏洞,怎么防护0-day漏洞攻击

随着信息技术的快速发展&#xff0c;网络安全问题日益凸显&#xff0c;其中0day漏洞攻击作为一种高级威胁手段&#xff0c;给企业和个人用户带来了极大的风险。下面德迅云安全就对0day漏洞攻击进行简单讲解下&#xff0c;并分享相应的一些安全措施&#xff0c;以期提高网络安全…

网络空间地图测绘理论体系白皮书(2023年)02网络空间测绘研究背景(想法比较好,着重看)

01前言 02 网络空间测绘研究背景 2.1 网络空间的起源 2.2 传统测绘理论 2.3 网络空间测绘相关工作 03 测绘体系框架概念定义 3.1 网络空间 3.2 网络空间地图测绘 3.3 体系框架总体思路 04 测绘体系框架应用实践 4.1 网络空间地形图 4.2 网络空间地志图 4.3 网络空间战略图 05 总…

Python 全栈安全(一)

原文&#xff1a;annas-archive.org/md5/712ab41a4ed6036d0e8214d788514d6b 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 前言 序言 多年前&#xff0c;我在亚马逊搜索了一本基于 Python 的应用程序安全书。我以为会有多本书可供选择。已经有了很多其他主题的 Pyt…

【ZYNQ】Zynq 芯片介绍

Zynq 是 Xilinx 公司提出的全可编程 SoC 架构&#xff0c;集成了单核或多核 ARM 处理器与 Xilinx 16nm 或 28nm 可编程逻辑&#xff0c;包括 Zynq 7000 Soc&#xff0c;Zynq UltraScale MPSoC 和 Zync UltraScale RFSoC 等系列。本文主要介绍 Xilinx Zynq 7000 系列芯片架构、功…

Hadoop1X,Hadoop2X和hadoop3X有很大的区别么?

Hadoop的演进从Hadoop 1到Hadoop 3主要是为了提供更高的效率、更好的资源管理、更高的可靠性以及对更多数据处理方式的支持。下面是Hadoop 1, Hadoop 2, 和 Hadoop 3之间的主要区别和演进的原因&#xff1a; Hadoop 1 特点&#xff1a; 主要包括两大核心组件&#xff1a;HDFS&a…

simple-jwt快速入门(包含自定制)

simple-jwt快速入门(包含自定制) 目录 simple-jwt快速入门(包含自定制)安装路由层视图层全局配置前端传入参数配置文件定制登录返回格式定制payload格式自定制签发-认证 安装 pip install djangorestframework-simplejwt路由层 from rest_framework_simplejwt.views import T…

【 AIGC 研究最新方向(上)】面向平面、视觉、时尚设计的高可用 AIGC 研究方向总结

目前面向平面、视觉、时尚等设计领域的高可用 AIGC 方向有以下 4 种&#xff1a; 透明图层生成可控生成图像定制化SVG 生成 本篇&#xff08;上篇&#xff09;介绍 1、2&#xff0c;而下篇将介绍 3、4。 透明图层生成 LayerDiffuse 代表性论文&#xff1a;Transparent Imag…

Qt基础之四十六:Qt界面中嵌入第三方程序的一点心得

本文主要讲解QWidget和QWindow的区别,以及如何在QWidget中嵌入第三方程序,并完美解决在QWidget中嵌入某些程序(比如Qt程序)时出现的白边问题。 下面是嵌入QQ音乐的样子,这首歌还不错。 先用spy++查看QQ音乐的窗口信息,如果安装了Visual Studio,工具菜单里自带spy++ 然后…

Spring Boot | Spring Boot 默认 “缓存管理“ 、Spring Boot “缓存注解“ 介绍

目录: 一、Spring Boot 默认 "缓存" 管理 :1.1 基础环境搭建① 准备数据② 创建项目③ 编写 "数据库表" 对应的 "实体类"④ 编写 "操作数据库" 的 Repository接口文件⑤ 编写 "业务操作列" Service文件⑥ 编写 "applic…

Redis入门到通关之数据结构解析-QuickList

文章目录 ☃️前提概要☃️ 配置项相关☃️简要源码☃️总结 Redis中的 QuickList 是一种特殊的数据结构&#xff0c;用于存储列表类型的数据。它的设计目的是在内存中高效地存储和操作大量的列表元素&#xff0c;尤其是当列表长度很大时。 QuickList的内部结构是一个由多个节…

政安晨:【Keras机器学习示例演绎】(八)—— 利用 PointNet 进行点云分割

目录 简介 导入 下载数据集 加载数据集 构建数据集 预处理 创建 TensorFlow 数据集 PointNet 模型 排列不变性 变换不变性 点之间的相互作用 实例化模型 训练 直观了解培训情况 推论 最后说明 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论…