Springboot 那年我双手插兜,手写一个excel导出

news/2024/11/29 20:47:58/

前言

其实就是利用了csv 和txt  文件转换 。

 

不多说,开始玩代码。

正文


本篇内容:


① 了解根本生成excel内容的CSV文件玩法

② 手动拼接文本演示

③ 项目内实战写法,从数据库到导出

④ 解决list数据过多,使用分批分页处理生成csv (EXCEL)
 

思路:

创建csv文件, 往里面写入符合转换成csv文件的内容 即可。

① 了解根本生成excel内容的CSV文件玩法

先看看什么原理 :

首先我们创建一个csv文件



 

然后打开里面填充一些数据:
 

 

然后反手把文件后缀改成.txt 看看里面是啥 :
 

 

看看里面:
 

 

② 手动拼接文本演示

 

工具类 :

MyCsvFileUtil.java

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;@Slf4j
public class MyCsvFileUtil {public static final String CSV_DELIMITER = ",";public static final String CSV_TAIL = "\r\n";/*** 将字符串转成csv文件*/public  static  void createCsvFile(String savePath,String contextStr) throws IOException {File file = new File(savePath);//创建文件file.createNewFile();//创建文件输出流FileOutputStream fileOutputStream = new FileOutputStream(file);//将指定字节写入此文件输出流fileOutputStream.write(contextStr.getBytes("gbk"));fileOutputStream.flush();fileOutputStream.close();}/*** 写文件** @param fileName* @param content*/public static void writeFile(String fileName, String content) {FileOutputStream fos = null;OutputStreamWriter writer = null;try {fos = new FileOutputStream(fileName, true);writer = new OutputStreamWriter(fos, "GBK");writer.write(content);writer.flush();} catch (Exception e) {log.error("写文件异常|{}", e);} finally {if (fos != null) {IOUtils.closeQuietly(fos);}if (writer != null) {IOUtils.closeQuietly(writer);}}}

演示拼接调用工具类生成CSV文件:
 

    @RequestMapping("/createCsvFileTest")public void doTest() throws IOException {//存放地址String path = "D:\\mycsv\\test.csv";String word = "";//表头固定好String tableNames = "CODE,NAME,PARENT_CODE,FULL_NAME";//数据内容。String oneRows = "110100,北京市,110000,北京北京市";String twoRows = "110101,东城区,110100,北京北京市东城区";String threeRows = "110102,西城区,110100,北京北京市西城区";String fourRows = "110105,朝阳区,110100,北京北京市朝阳区";//拼接word += tableNames + "\r\n";word += oneRows+ "\r\n";word += twoRows+ "\r\n";word += threeRows+ "\r\n";word += fourRows+ "\r\n";//调用方法生成MyCsvFileUtil.createCsvFile(path,word);}

代码简析:

调用这个示例接口,看看效果:

 

 

 

③ 项目内实战写法,从数据库到导出

接口使用写法:
 

    @RequestMapping("/createCsvFileTest2")public void createCsvFileTest2() throws IOException {List<District> districts = districtMapper.queryByParentCodes(Arrays.asList("110100"));//存放地址&文件名String fileName = "D:\\mycsv\\test2.csv";String tableNames = "CODE,NAME,PARENT_CODE,FULL_NAME"+MyCsvFileUtil.CSV_DELIMITER;//创建文件MyCsvFileUtil.createCsvFile(fileName,tableNames);//写入数据String contentBody =buildCsvFileBody(districts);//调用方法生成MyCsvFileUtil.createCsvFile(fileName,contentBody);}

解析数据list 做内容拼接处理 函数:
 

ps: 表头 多少个,字段就多少个, 默认值赋值啥的,格式转换啥的都可以,只要每一行数量对的上即可,每一个最后结尾的标记就是 String CSV_TAIL = "\r\n"

    @RequestMapping("/createCsvFileTest2")public void createCsvFileTest2() throws IOException {List<District> districts = districtMapper.queryByParentCodes(Arrays.asList("110100"));//存放地址&文件名String fileName = "D:\\mycsv\\test2.csv";String tableNames = "CODE,NAME,PARENT_CODE,FULL_NAME"+MyCsvFileUtil.CSV_TAIL;//创建文件MyCsvFileUtil.writeFile(fileName,tableNames);//写入数据String contentBody =buildCsvFileBody(districts);//调用方法生成MyCsvFileUtil.writeFile(fileName,contentBody);}

 

 

事不宜迟,看看接口调用效果:

可以看到从数据量里面查出来这 16条数据,list集合:

 

最后转换生成文件:
 

 

 可以看到成功出货:


 

那么问题来了,如果我们需要导出的数据条数很多,拼接的contentBody 会非常长。

那么我们就需要考虑分批查询、分批拼接处理、分批写入,按照实际业务场景和数据长度去考量,每一批的限制。 甚至还可以实现拼接好每一批,然后慢慢再根据当前批次ID,做顺序写入。

④ 解决list数据过多,使用分批分页处理生成csv (EXCEL)

抛转引玉,给大家整个简单的分批玩法,生成csv 。

改造点 1  , 整一个分页查询。

老面孔手动分页DTO:

import lombok.Data;/*** @Author: JCccc* @Date: 2022-6-15 16:53* @Description:*/
@Data
public class PageLimitDTO {private Integer pageSize;private Integer currIndex;}

工具类  MyPageCutUtil.java

import lombok.extern.slf4j.Slf4j;import java.util.LinkedList;
import java.util.List;@Slf4j
public class MyPageCutUtil {public static List<PageLimitDTO> getPageLimitGroupList(Integer totalCount, Integer batchSizeLimit) {log.info("这一次处理的总数据条数为 ={} 条, 每一批次处理条数为 ={} 条,现在开始做分批切割处理。",totalCount,batchSizeLimit);int pageNum = totalCount / batchSizeLimit;int surplus = totalCount % batchSizeLimit;if (surplus > 0) {pageNum = pageNum + 1;}List<PageLimitDTO> pageLimitGroupList =new LinkedList<>();for(int i = 0; i < pageNum; i++){Integer currIndex = i * batchSizeLimit;PageLimitDTO pageLimitDTO=new PageLimitDTO();pageLimitDTO.setPageSize(batchSizeLimit);pageLimitDTO.setCurrIndex(currIndex);pageLimitGroupList.add(pageLimitDTO);log.info("分批切割,第={}次,每次={}条,最终会处理到={}条。",pageLimitGroupList.size(),batchSizeLimit,currIndex+batchSizeLimit);}log.info("这一次处理的总数据条数为 ={} 条, 每一批次处理条数为 ={} 条,总共切割分成了 ={} 次,一切准备就绪。",totalCount,batchSizeLimit,pageLimitGroupList.size());return pageLimitGroupList;}}

老面孔手动分页统计sql :

mapper 简单打个样

/*** 统计所有符合搜索条件的数据* @param codeList* @return*/
int  getCountAllList(@Param("codeList") List<String> codeList);

对应xml 的sql 

<select id="getCountAllList" resultType="java.lang.Integer">SELECTCOUNT(*)FROM district_infoWHERE PARENT_CODE IN<foreach collection="codeList" item="code" open="(" separator="," close=")">#{code}</foreach></select>

老面孔手动分页查询sql:
 

/*** 手动分页查询* @param codeList* @param currIndex* @param pageSize* @return*/
List<District> getPageList(@Param("codeList") List<String> codeList,Integer currIndex,Integer pageSize);

sql:

<select id="getPageList" resultMap="BaseResultMap">SELECT<include refid="Base_Column_List"/>FROM district_info<where><if test="codeList != null and  !codeList.isEmpty()">PARENT_CODE  IN<foreach collection="codeList" item="code" open="(" separator="," close=")">#{code}</foreach></if></where>LIMIT #{currIndex} , #{pageSize}
</select>

ok接下来继续改造我们的分批查询,处理数据:

 

    @RequestMapping("/createCsvFileTest3")public void createCsvFileTest3() throws IOException {//存放地址&文件名String fileName = "D:\\mycsv\\test3.csv";String tableNames = "地域编码,地域名称,父级编码,地域全称" + MyCsvFileUtil.CSV_TAIL;//创建文件MyCsvFileUtil.writeFile(fileName, tableNames);//获取数据总计数Integer totalCount = districtMapper.getCountAllList(null);//每批同步的数据条数Integer batchSizeLimit = 500;//分批切割处理List<PageLimitDTO> pageLimitGroupList = MyPageCutUtil.getPageLimitGroupList(totalCount, batchSizeLimit);int count = 1;//物理批次查询for (PageLimitDTO pageBatchLimit : pageLimitGroupList) {List<District> pageBatchList = districtMapper.getPageList(null, pageBatchLimit.getCurrIndex(), pageBatchLimit.getPageSize());if (!CollectionUtils.isEmpty(pageBatchList)) {//写入数据String contentBody = buildCsvFileBody(pageBatchList);//调用方法生成MyCsvFileUtil.writeFile(fileName, contentBody);}log.info("第{}批次,District数据处理结束执行", count);count = count + 1;}}

代码简析: 

 

OK,调用一下接口,看看效果:

 打开看看数据:

 

 

 

ps:还有没有优化封装余地?
有的,文中讲到了,还可以考虑加上整个大批次的ID,然后考虑并行查询,并行拼接后,再按顺序插入。
有想法的还可以写个注解,标记相关表头别名,是否参与导出,然后再拼接的时候魔改一手,反射自动拿字段属性等等。

好了该篇就到这。 

 


http://www.ppmy.cn/news/3298.html

相关文章

RK3568平台开发系列讲解(Linux系统篇)内存映射与虚拟内存

🚀返回专栏总目录 文章目录 一、内存映射二、内存保护三、内存锁定3.1、锁定指定的内存段3.2、锁定进程的所有内存页沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇章讲介绍几个对虚拟内存进行系统级控制的系统调用,合理使用它们,能极大地提高应用的执行效率,…

基于安卓的校园订餐系统开发设计

目 录 Abstract 6 1 绪 论 1 1.1 研究背景 1 1.2 研究意义 1 1.3 国内外研究现状 1 1.4 研究的过程和结果 2 1.5 论文主要组织结构 3 2 设计原理和方法 4 2.2 关键技术简介 4 2.3 开发工具 5 2.4 应用平台 6 3 需求分析 8 3.1 功能性需求 8 3.2 非功能需求 17 4 系统概要设计 1…

人工智能:智能语音技术应用场景介绍

❤️作者主页&#xff1a;IT技术分享社区 ❤️作者简介&#xff1a;大家好,我是IT技术分享社区的博主&#xff0c;从事C#、Java开发九年&#xff0c;对数据库、C#、Java、前端、运维、电脑技巧等经验丰富。 ❤️个人荣誉&#xff1a; 数据库领域优质创作者&#x1f3c6;&#x…

Qt-Web混合开发-QWebEngineView加载网页最小示例(2)

Qt-Web混合开发-QWebEngineView加载网页最小示例 2&#x1f30d; 文章目录Qt-Web混合开发-QWebEngineView加载网页最小示例 2&#x1f30d;1、概述&#x1f310;2、实现效果&#x1f9ed;3、实现功能&#x1f685;4、关键代码&#x1f69a;5、源代码&#x1f680;更多精彩内容&…

Git系列之git tag

概述 Git可给仓库历史中的某一个提交打上标签&#xff0c;常用于标记发布结点。 本文使用的Git版本为&#xff1a; $ git --version git version 2.23.0.windows.1实战 列出标签 列出全部标签的命令为&#xff1a;git tag或git tag -l或git tag -list&#xff0c;即-l或-l…

Jmeter初了解-接口并发测试

Jmeter初了解-接口并发测试 介绍 我们在开发的时候&#xff0c;经常会需要进行接口压力测试&#xff0c;确定接口运行的稳定情况 这里我们就使用java开发的测试工具Jmeter来进行测试。 Jmeter 官网地址 Apache JMeter™应用程序是开源软件&#xff0c;是一个 100% 纯 Jav…

DDPM = 拆楼 + 建楼

Contents拆楼 (加噪)建楼 (生成)降低方差递归生成超参设置References下面将从 “拆楼-建楼” 的通俗类比中介绍生成扩散模型 DDPM&#xff0c;在这个视角中&#xff0c;我们可以通过较为 “大白话” 的描述以及比较少的数学推导&#xff0c;来得到跟原始论文一模一样的结果。总…

新手入门:调环境,快逼到玄学了该怎么办

又开始坐下来写东西的感觉真好再次说说调环境新学生的新问题以ipyvolume的调试为例再次说说调环境 新手入门&#xff0c;最难的莫过于环境安装。老手入新坑&#xff0c;亦是如此。 今天结合近两天的经历&#xff0c;再聊聊这个话题吧。 新学生的新问题 每年面对新到组里的孩…