使用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 {@Resourceprivate 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;}@Overridepublic void invoke(Map<Integer, String> data, AnalysisContext context) {// 数据add进入集合dataList.add(data);// size是否为2000条:这里其实就是分批.当数据等于2k的时候执行一次写入excelif (dataList.size() >= ExcelConstants.PER_WRITE_EXCEL_ROW_COUNT) {save2Excel();// 清理集合便于GC回收dataList.clear();}}@Overridepublic 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 中所有数据解析完毕会调用此方法*/@Overridepublic 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());}@Overridepublic void execute(Runnable task) {showThreadPoolInfo("1. do execute");super.execute(task);}@Overridepublic void execute(Runnable task, long startTimeout) {showThreadPoolInfo("2. do execute");super.execute(task, startTimeout);}@Overridepublic Future<?> submit(Runnable task) {showThreadPoolInfo("1. do submit");return super.submit(task);}@Overridepublic <T> Future<T> submit(Callable<T> task) {showThreadPoolInfo("2. do submit");return super.submit(task);}@Overridepublic ListenableFuture<?> submitListenable(Runnable task) {showThreadPoolInfo("1. do submitListenable");return super.submitListenable(task);}@Overridepublic <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")@Overridepublic 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: truemax-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.mzph.cn/diannao/1548.shtml

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

相关文章

12.Hexo helpers类似函数和data folder数据文件夹

helper Hexo里的helper&#xff0c;或者说是函数 基本上就是小函数&#xff0c;可以在layout布局中使用&#xff0c;可以允许做一些事情 如字符串操作、检查true或false、检查是否在一个页面上、打印出某个页面中的日期或时间特定格式 打开index.ejs trim 可以通过 <%…

向量数据库的崛起:如何改变数据存储与机器学习的未来

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

ExpertPrompting:指导大语言模型成为杰出专家

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 论文标题&#xff1a;ExpertPrompting: Instructing Large Language Models to be Distinguished Experts 论文地址&#xff1a;https://arxiv.org/abs/2305.14688 作者 & 机构&#xff1a;Benfen…

Linux嵌入式驱动开发-阻塞IO与非阻塞IO

文章目录 阻塞与非阻塞访问简介阻塞访问的实现等待队列等待队列头等待队列项从等待队列头添加/移除等待队列项等待唤醒等待事件API 非阻塞访问的实现轮询poll 函数原型可以返回的资源状态 阻塞与非阻塞访问简介 **IO&#xff1a;**Input/Output&#xff0c;也就是输入/输出&am…

Mysql学习大纲

文章目录 整体大纲总结 整体大纲 大纲 MySQL在金融互联网行业的企业级安装部署mysql启动关闭原理和实战&#xff0c;及常见错误排查 花钱9.9 订阅了专栏MySQL字符集和校对规则史上最详细的Mysql用户权原理和实战&#xff0c;生产案例InnoDB引擎原理和实战&#xff0c;通俗易懂…

IoT、IIoT、AIoT的区别是什么?

一、IoT、IIoT、AIoT的区别是什么&#xff1f; IoT、IIoT和AIoT都是物联网&#xff08;Internet of Things&#xff09;的不同应用和发展方向&#xff0c;但它们之间存在一些区别。 IoT&#xff08;物联网&#xff09;&#xff1a;物联网是指通过互联网连接各种物理设备&#x…

【Linux】小知识点温习---命令

许多常见命令会用&#xff0c;但是很少注意他们的区别&#xff1b;亦或在学习中使用较少&#xff0c;容易忘记&#xff0c;今天做一个回顾。 ls系列 -a:显示所有文件&#xff08;包括隐藏文件&#xff09; -l:将文件以竖列形式显示 -i&#xff1a;显示文件的inode编号 pwd 显…

MacOS 文件系统种类及介绍

MacOS 文件系统种类 详细介绍 详细介绍 从图片中我们可以看到一个文件系统选择器的界面&#xff0c;列出了多种不同的文件系统选项。这些文件系统各有其特点和用途&#xff0c;以下是它们之间的主要区别&#xff1a; APFS&#xff1a;Apple File System&#xff0c;是苹果公司为…

车载电子电器架构 —— 功能安全开发(首篇)

车载电子电器架构 —— 功能安全开发 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己…

江西智博环境| 邀您参加2024全国水科技大会暨技术装备成果展览会

展位号&#xff1a;A28 企业介绍 江西智博环境技术有限公司始创于2008年初&#xff0c;总部位于江西省域副中心城市-赣州。公司主要从事一体化净水设备、单村供站、泵船、无负压供水设备自动化控制系统、低配电系统、工艺设备及智慧水务的设计研发、生产、销售、安装、调试等业…

WordPress SQLite Docker 镜像封装细节

为了让大家用的放心&#xff0c;同时解答 GitHub 社区中的疑问。这篇文章聊聊上一篇文章的 Docker 容器封装细节。 写在前面 在前一篇文章《WordPress 告别 MySQL&#xff1a;Docker SQLite WordPress》中&#xff0c;如果你跟着文章实践&#xff0c;大概三分钟就能够启动一个…

【批量区域识别内容重命名】批量识别图片区域文字并重命名,批量图片部分识别内容重命文件,PDF区域识别提取重命名

我们在工作和生活中经常遇到这样的需求&#xff1a;比如将以下的图片区域识别进行重命名&#xff0c;批量识别后改成以时间和工作内容重命名&#xff0c;便于日后检索&#xff0c;快速查询 首先我们拍摄照片用到的是水印相机&#xff0c;这里的文字呢我们需要加个背景&#xff…

华为机考入门python3--(16)牛客16-购物单最大满意度

分类&#xff1a;动态规划&#xff0c;组合&#xff0c;最大值&#xff0c;装箱问题 知识点&#xff1a; 生成递减数 100, 90, 80, ..., 0 range(100, -1, -10) 访问列表的下标key for key, value in enumerate(my_list): 动态规划-捆绑装箱问题 a. 把有捆绑约束的物…

AI大模型日报#0421:「个性化」图像Gen4Gen框架、吴恩达亲授智能体设计模式、国内14大LLM最新评测报告

导读&#xff1a; 欢迎阅读《AI大模型日报》&#xff0c;内容基于Python爬虫和LLM自动生成。目前采用“文心一言”生成了每条资讯的摘要。 标题: 小冰徐元春&#xff1a;AIGC已经让普通人开始赚钱 | 中国AIGC产业峰会 摘要: 要点提炼&#xff1a; 在中国AIGC产业峰会上&…

Llama 3大模型发布!快速体验推理及微调

Meta&#xff0c;一家全球知名的科技和社交媒体巨头&#xff0c;在其官方网站上正式宣布了一款开源的大型预训练语言模型——Llama-3。 据了解&#xff0c;Llama-3模型提供了两种不同参数规模的版本&#xff0c;分别是80亿参数和700亿参数。这两种版本分别针对基础的预训练任务…

2024mac苹果电脑如何清理磁盘空间?用什么软件最好

苹果电脑已成为我们日常生活和工作不可或缺的一部分。随着时间的推移&#xff0c;不论是办公文档、个人照片还是各式各样的应用程序&#xff0c;都会逐渐积累&#xff0c;导致电脑的磁盘空间日益紧张。对于用户来说&#xff0c;苹果电脑如何清理磁盘空间&#xff0c;以保持设备…

【2024年最新】NodeMCU-ESP8266刷AT固件教程——适用于esp-12E和esp-12F

硬件图片 原理图 0、工具打包下载 工具包 密码:keduo 1、工具及固件下载 固件下载地址&#xff1a; 欢迎 | 安信可科技 (ai-thinker.com) 下载以下固件&#xff1a; 直接下载地址&#xff1a;AT 固件&#xff08;固件号&#xff1a;0781&#xff09; 下载以下工具&#xf…

使用Nexus搭建npm私服库

优质博文&#xff1a;IT-BLOG-CN 【1】下载nexus http://www.sonatype.com/download-oss-sonatype解压到本地即可&#xff1b; 【2】打开nexus-3.2.0-01-win64\nexus-3.2.0-01\bin&#xff1b;打开cmd&#xff08;必须使用cmd&#xff09; 执行nexus.exe /run&#xff1b;需要使…

Tomcat弱口令及war包漏洞复现(保姆级教程)

1.环境搭建 靶机&#xff1a;Ubuntu 安装参考&#xff1a;安装Ubuntu详细教程_乌班图安装教程-CSDN博客 vulhub docker搭建tomcat漏洞环境 参考&#xff1a;vulhub docker靶场搭建-CSDN博客 工具&#xff1a;burpsuite 2.漏洞复现 2.1弱口令爆破 进入http://192.168.143…

分类神经网络2:ResNet模型复现

目录 ResNet网络架构 ResNet部分实现代码 ResNet网络架构 论文原址&#xff1a;https://arxiv.org/pdf/1512.03385.pdf 残差神经网络(ResNet)是由微软研究院的何恺明、张祥雨、任少卿、孙剑等人提出的&#xff0c;通过引入残差学习解决了深度网络训练中的退化问题&#xff…