架构(十二)动态Excel

一、引言

        作者最近的平台项目需要生成excel,excel的导入导出是常用的功能,但是作者想做成动态的,不要固定模板,那就看看怎么实现。

二、后端

        先捋一下原理,前后端的交互看起来是制定好的接口,其实根本上是数据键值对的映射,后端可以直接用Map进行接收,只不过接收回来的数据如果是对象嵌套对象或者集合嵌套,那么就要用object接收之后再解析。

        而对于excel的导入导出来说,基本上都是转字符串再去填入文件,也不会有什么嵌套,所以可以直接用Map接收。

1、pom

        先引入工具包

<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>3.17</version><exclusions><exclusion><groupId>org.apache.xmlbeans</groupId><artifactId>xmlbeans</artifactId></exclusion></exclusions></dependency>

2、导入

        导入按功能不同,步骤也不一样,如果是为了业务处理,那就是把excel数据解析之后处理完,前端再去查

        作者这边是解析完excel之后把数据直接给前端,一个意思,主要是解析excel

        首先要把excel给下载下来

@Service
public class ExcelWDownloadUtil {private static final LoggerService LOG = LoggerServiceFactory.getLoggerService(FileWsHelper.class);private static final String TITLE = "ExcelWDownloadUtil";/*** Function - 下载文件** @param fileUrl 文件路径* @return 文件内容*/public byte[] downloadBytes(String fileUrl) {HttpGet httpGet = new HttpGet(fileUrl);return HttpClientHelper.getInstance().getBytes(httpGet);}/*** excel文件后缀*/private static final String EXCEL_FIX = "xlsx";private static final String EXCEL_FIX_OLD = "xls";/*** 文件后缀分隔符*/private final static String FILE_SPLIT = ".";public List<List<String>> downloadExcel(String excelDownloadUrl) {// 1. 通过http下载文件,并转为bytesbyte[] fileBytes = downloadBytes(excelDownloadUrl);// 2. 将byte数组转为流ByteArrayInputStream byteInputStream = new ByteArrayInputStream(fileBytes);// 3. 将流转为excel工作薄String fileType = getFileType(excelDownloadUrl);if (StringUtilsExt.equals(fileType, EXCEL_FIX)) {return convertXlsxExcel(byteInputStream);} else if (StringUtilsExt.equals(fileType, EXCEL_FIX_OLD)) {return convertXlsExcel(byteInputStream);}LOG.error(TITLE, "file is not excel");return null;}public List<List<String>> convertXlsxExcel(ByteArrayInputStream byteInputStream) {List<List<String>> res = new ArrayList<>();XSSFWorkbook sheets = null;try {// 1. 转为工作薄sheets = new XSSFWorkbook(byteInputStream);// 2. 取第一个SheetXSSFSheet sheet = sheets.getSheetAt(0);// 3. 循环行列,转为String返回DataFormatter formatter = new DataFormatter();for (int i = 0; i <= sheet.getLastRowNum(); i++) {List<String> rowString = getStringFormRow(sheet.getRow(i), formatter);if (CollectionUtilsExt.isNotBlank(rowString)) {res.add(rowString);}}} catch (IOException e) {LOG.error(TITLE, e);throw new FileExecuteException("excel file io exception");} finally {// 关闭文件流if (sheets != null) {try {sheets.close();} catch (IOException e) {LOG.error(TITLE, e);}}}return res;}public List<List<String>> convertXlsExcel(ByteArrayInputStream byteInputStream) {List<List<String>> res = new ArrayList<>();HSSFWorkbook sheets = null;try {// 1. 转为工作薄sheets = new HSSFWorkbook(byteInputStream);// 2. 取第一个SheetHSSFSheet sheet = sheets.getSheetAt(0);// 3. 循环行列,转为String返回DataFormatter formatter = new DataFormatter();for (int i = 0; i < sheet.getLastRowNum(); i++) {List<String> rowString = getStringFormRow(sheet.getRow(i), formatter);if (CollectionUtilsExt.isNotBlank(rowString)) {res.add(rowString);}}} catch (IOException e) {LOG.error(TITLE, e);throw new FileExecuteException("excel file io exception");} finally {// 关闭文件流if (sheets != null) {try {sheets.close();} catch (IOException e) {LOG.error(TITLE, e);}}}return res;}private List<String> getStringFormRow(Row row, DataFormatter formatter) {if (Objects.isNull(row)) {return null;}List<String> rowString = new ArrayList<>();for (int j = 0; j < row.getLastCellNum(); j++) {rowString.add(getStringFromCell(row.getCell(j), formatter));}return rowString;}private String getStringFromCell(Cell cell, DataFormatter formatter) {if (Objects.isNull(cell)) {return null;}if (CellType.NUMERIC == cell.getCellTypeEnum()) {BigDecimal num = BigDecimal.valueOf(cell.getNumericCellValue());// 判断是否有小数,防止1变成了1.0,下游会报错if (new BigDecimal(num.intValue()).compareTo(num) == 0) {return String.valueOf(num.intValue());}// 这里是防止出现科学计数法return NumberToTextConverter.toText(cell.getNumericCellValue());} else {return formatter.formatCellValue(cell);}}public static String getFileType(String fileName) {if (StringUtilsExt.isBlank(fileName) || !fileName.contains(FILE_SPLIT)) {return null;}return fileName.substring(fileName.lastIndexOf(FILE_SPLIT) + 1);}}

        解析成键值对,说白了解析excel得到的List<List<String>>,第一行是列名作为键,下面行数据都作为值

if (CollectionUtilsExt.isBlank(fileList) || fileList.size() <= 1) {throw new OrderException("EXCEL_NO_DATA");}List<Map<String, String>> res = new ArrayList<>();List<String> cellName = fileList.get(0);for (int i = 1; i < fileList.size(); i++) {List<String> row = fileList.get(i);Map<String, String> rowMap = new HashMap<>();for (int j = 0; j < row.size(); j++) {rowMap.put(cellName.get(j), row.get(j));}res.add(rowMap);}return res;


        

3、导出

        导出的话就是把数据生成excel,第一把前端传的数据或者数据库查出来的数据生成excel,第二步把excel上传内部服务器,第三步把生成文件的地址给前端打开

        生成excel

@Service
public class GenerateExcelUtil {private static final int SHEET_ROW = 1000;private static final short FONT_SIZE = 11;private int getCellWidth(String cellName) {// 根据列名获取配置的列宽度,不配置也行,默认宽度Map<String, String> cellWidthMap = Config.getMap(CELL_WIDTH_MAP);if (cellWidthMap == null || !cellWidthMap.containsKey(cellName)) {return 4000;}return Integer.parseInt(cellWidthMap.get(cellName));}private short getCellColor(String cellName) {// 根据列名获取配置的列颜色,不配置也行,默认颜色Map<String, String> cellColorMap = Config.getMap(CELL_COLOR_MAP);if (cellColorMap == null || !cellColorMap.containsKey(cellName)) {return 0;}return Short.parseShort(cellColorMap.get(cellName));}public SXSSFWorkbook generateExcel(String sheetName, List<Map<String, String>> excelBoList,List<String> cellNameList) {// 生成excel文件SXSSFWorkbook workbook = new SXSSFWorkbook(SHEET_ROW);// 建表SXSSFSheet sheet = workbook.createSheet(sheetName);// 设置每列宽度for (int i = 0; i < cellNameList.size(); i++) {sheet.setColumnWidth(i, getCellWidth(cellNameList.get(i)));}// 构建表头SXSSFRow rowHead = sheet.createRow(0);for (int i = 0; i < cellNameList.size(); i++) {createCell(rowHead, i, cellNameList.get(i), createTitleStyle(workbook, getCellColor(cellNameList.get(i))));}// 构建内容CellStyle contentStyle = createContentStyle(workbook);for (int i = 0; i < excelBoList.size(); i++) {Map<String, String> excelBo = excelBoList.get(i);createRow(sheet, i + 1, excelBo, contentStyle, cellNameList);}return workbook;}private CellStyle createTitleStyle(SXSSFWorkbook workbook, short color) {Font boldFont = workbook.createFont();boldFont.setFontHeightInPoints(FONT_SIZE);boldFont.setBold(true);boldFont.setColor(color);CellStyle style = workbook.createCellStyle();style.setFont(boldFont);style.setWrapText(true);style.setAlignment(HorizontalAlignment.CENTER);style.setVerticalAlignment(VerticalAlignment.CENTER);style.setBorderBottom(BorderStyle.THIN);style.setBorderLeft(BorderStyle.THIN);style.setBorderRight(BorderStyle.THIN);style.setBorderTop(BorderStyle.THIN);return style;}private CellStyle createContentStyle(SXSSFWorkbook workbook) {Font boldFont = workbook.createFont();boldFont.setFontHeightInPoints(FONT_SIZE);CellStyle style = workbook.createCellStyle();style.setFont(boldFont);style.setWrapText(true);style.setAlignment(HorizontalAlignment.CENTER);style.setVerticalAlignment(VerticalAlignment.CENTER);return style;}private void createCell(SXSSFRow row, int column, Object value, CellStyle style) {SXSSFCell cell = row.createCell(column);cell.setCellType(CellType.STRING);cell.setCellValue(Null.or(value, Object::toString, null));cell.setCellStyle(style);}private void createRow(SXSSFSheet sheet, int rowIndex, Map<String, String> map, CellStyle style,List<String> cellName) {SXSSFRow row = sheet.createRow(rowIndex);for (int i = 0; i < cellName.size(); i++) {createCell(row, i, map.get(cellName.get(i)), style);}}}

        上传服务器 

@Service
public class ExcelUploadUtil {private static final LoggerService LOG = LoggerServiceFactory.getLoggerService(ExcelUploadUtil.class);private static final String TITLE = "ExcelUploadUtil";/*** 上传文件的地址*/private static final String UPLOAD_URL = "fileUploadUrl";/*** 上传文件的contentType*/private static final String EXCEL_CONTENT_TYPE ="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";/*** 创建文件的后缀*/public static final String FILE_SUFFIX = ".xlsx";/*** 请求头*/private static final String CONTENT_TYPE = "Content-Type";public FileResponseBo uploadExcel(SXSSFWorkbook workbook, String filePrefix) {File file = convertFile(workbook, filePrefix);LOG.info(TITLE, "convertFile");if (file == null) {return null;}return uploadFile(file, EXCEL_CONTENT_TYPE);}private File convertFile(SXSSFWorkbook workbook, String filePrefix) {File file = null;FileOutputStream fos = null;try {file = File.createTempFile(filePrefix, FILE_SUFFIX);fos = new FileOutputStream(file);workbook.write(fos);} catch (IOException e) {// 这里上传文件有io异常无需处理,后续返回空,会对空处理LOG.error(TITLE, e);} finally {// 关闭文件流if (fos != null) {try {fos.close();} catch (IOException e) {LOG.error(TITLE, e);}}// 删除临时xml文件workbook.dispose();}return file;}/*** 上传文件* * @param file* @param contentType* @return*/private FileResponseBo uploadFile(File file, String contentType) {String uploadUrl = Config.get(UPLOAD_URL);LOG.info(TITLE, "uploadUrl:{}", uploadUrl);HttpPost httpPost = new HttpPost(uploadUrl);httpPost.setHeader(CONTENT_TYPE, contentType);FileEntity fileEntity = new FileEntity(file);httpPost.setEntity(fileEntity);String res = HttpClientHelper.getInstance().doPost(httpPost);LOG.info(TITLE, "res:{}", res);return JSONUtil.parse(res, FileResponseBo.class);}}

        前端使用Window.open就可以打开下载了

三、前端

        前端可以参考前端(一)Vue+Java实现动态表格展示_java+vue显示数据库数据-CSDN博客

四、效果

        只要导入导出的数据变一下,表格就会自动展示不同的列和数据

002b3a272e1244638e4bb87423ebc406.png

五、总结

        很多东西做成通用的会比较方便,但是比较适合内部项目,减少人力

 

 

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

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

相关文章

用EXCEL从地址(上海)中提取各区(浦东新区等区)信息

背景&#xff1a; 朋友工作需要经常用EXCEL把各上海用户收货地址中的区提取出来&#xff0c;之前一直手动处理&#xff0c;希望我帮忙用EXCEL公式直接提取处理。 数据样式&#xff1a; 中国上海市浦东新区A小区 上海徐汇区B小区 中国&#xff0c;上海&#xff0c;浦东新区&a…

SpringSecurity个人学习笔记

SpringSecurity个人学习笔记 **项目学习地址 [springsecurity-demo](https://gitee.com/guzhuangzhuang/springsecuity-demo/tree/master/)**一、简介二、快速入门2.1 准备工作 三、登录认证3.1登录校验流程3.2原理初探3.2.1SpringSecurity完整流程3.2.2SpringSecurity用户认证…

极狐GitLab 与钉钉的集成实践

DingTalk OAuth 2.0 OmniAuth provider * 引入于 14.5 版本。 您可以使用您的钉钉账号登录极狐GitLab。 登录钉钉开放平台&#xff0c;创建应用。钉钉会生成一个客户端 ID 和密钥供您使用。 登录钉钉开放平台。 在顶部栏上&#xff0c;选择 应用程序开发 > 企业内部开发&am…

ECMAScript Modules规范的示例详解

ECMAScript Modules&#xff08;ESM&#xff09;是JavaScript中用于模块化开发的规范&#xff0c;它允许开发者将代码分割成多个独立的文件&#xff0c;以提高代码的可维护性和可重用性。下面是一个ECMAScript Modules规范的示例详解&#xff1a; 创建模块 1.1 导出变量 在一个…

单片机学习路线(简单介绍)

学习单片机对于电子爱好者和未来的嵌入式系统工程师来说是一段激动人心的旅程。单片机因其强大的功能、灵活性以及在各种智能设备中的广泛应用&#xff0c;成为了电子和计算机科学领域一个不可或缺的组成部分。如果你对如何开始这段旅程感到好奇&#xff0c;那么你来对地方了。…

【漏洞复现】狮子鱼CMS某SQL注入漏洞

Nx01 产品简介 狮子鱼CMS&#xff08;Content Management System&#xff09;是一种网站管理系统&#xff0c;它旨在帮助用户更轻松地创建和管理网站。该系统拥有用户友好的界面和丰富的功能&#xff0c;包括页面管理、博客、新闻、产品展示等。通过简单直观的管理界面&#xf…

vscode +git +gitee 文件管理

文章目录 前言一、gitee是什么&#xff1f;2. Gitee与VScode连接大概步骤 二、在vscode中安装git1.安装git2.安装过程3.安装完后记得重启 三、使用1.新建文件夹first2.vscode 使用 四、连接git1.初始化仓库2.设置git 提交用户和邮箱3.登陆gitee账号新建仓库没有的自己注册一个4…

基金是什么

一、基金是什么&#xff1f; 买基金就是委托别人帮我们投资&#xff0c;替我们买卖股票债券。 二、为什么委托别人&#xff1f; 因为我们不懂投资方面的知识&#xff0c;或者我们没有时间来做投资&#xff0c;那么就可以找专业人士帮我们投资。就像家长帮小孩报辅导班&#…

2.8日学习打卡----初学RabbitMQ(三)

2.8日学习打卡 一.springboot整合RabbitMQ 之前我们使用原生JAVA操作RabbitMQ较为繁琐&#xff0c;接下来我们使用 SpringBoot整合RabbitMQ&#xff0c;简化代码编写 创建SpringBoot项目&#xff0c;引入RabbitMQ起步依赖 <!-- RabbitMQ起步依赖 --> <dependency&g…

linux 07 存储管理

02. ext4是一种索引文件系统 上面是索引节点inode&#xff0c;存放数据的元数据 下面是存储块block&#xff0c;主要存放有关的信息 03.linux上的inode 查看文件中的inode ll -i 文件名 磁盘中的inode与文件数量 向sdb2中写文件&#xff1a; 结果&#xff1a; df -i 磁…

11 串口发送应用之使用状态机实现多字节数据发送

1. 使用串口发送5个字节数据到电脑 uart协议规定&#xff0c;发送的数据位只能是6&#xff0c;7&#xff0c;8位&#xff0c;如果数据位不符合&#xff0c;接收者接收不到数据。所以我们需要将40位数据data分为5个字节数据分别发送&#xff0c;那么接收者就能通过uart协议接收…

第一章:从3D到2D

本文是《从0开始图形学》的第一章内容。讲解如何将3D的模型“画”到2D的图形山。 概念解说 图形学渲染&#xff0c;就是将3D的东西“画”到2D的屏幕上&#xff0c;和拍照的效果是一样的&#xff0c;这也是为什么很多3D渲染引擎会有“相机”这个概念&#xff0c;这一节我们来看…

CleanMyMac4.14.7破解版安装包下载

CleanMyMac识别并清理垃圾文件的过程主要依赖于其强大的扫描功能和智能算法。以下是具体的步骤&#xff1a; CleanMyMac X2024全新版下载如下: https://wm.makeding.com/iclk/?zoneid49983 扫描垃圾文件&#xff1a;首先&#xff0c;用户需要打开CleanMyMac软件&#xff0c…

黄金交易策略(Nerve Nnife):大K线对技术指标的影响

我们使用heiken ashi smoothed来做敏感指标&#xff08;大趋势借助其转向趋势预判&#xff0c;但不是马上转变&#xff09;&#xff0c;has默认使用6根k线的移动平均值来做计算的。若在6根k线规范内有一个突变的行情&#xff08;k线很长&#xff09;&#xff0c;那么整个行情的…

【见微知著】OpenCV中C++11 lambda方式急速像素遍历

学习《OpenCV应用开发&#xff1a;入门、进阶与工程化实践》一书 做真正的OpenCV开发者&#xff0c;从入门到入职&#xff0c;一步到位&#xff01; C11 lambda语法 C11中引入了lambda表达式&#xff0c;它支持定义一个内联(inline)的函数&#xff0c;作为一个本地的对象或者…

JAVA毕业设计126—基于Java+Springboot+Vue的二手交易商城管理系统(源代码+数据库)

毕设所有选题&#xff1a; https://blog.csdn.net/2303_76227485/article/details/131104075 基于JavaSpringbootVue的二手交易商城管理系统(源代码数据库)126 一、系统介绍 本项目前后端分离&#xff0c;本系统分为管理员、用户两种角色 1、用户&#xff1a; 注册、登录、…

VBA技术资料MF117:测试显示器大小

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到…

使用Launch4j将jar包转成.exe可执行文件

Launch4j官网:Launch4j - Cross-platform Java executable wrapper 然后点击上面按钮 随便写个文件名

C++入门学习(二十六)for循环

for (初始化; 条件; 递增/递减) { // 代码块 } 打印1~10&#xff1a; #include <iostream> using namespace std; int main() { for (int i 1; i < 10; i) { cout <<i<<endl; } return 0; } 打印九九乘法表&#xff1a; #include <iostream…

修改SpringBoot中默认依赖版本

例如SpringBoot2.7.2中ElasticSearch版本是7.17.4 我希望把它变成7.6.1