架构(十二)动态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://www.mzph.cn/news/677344.shtml

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

相关文章

面试 JavaScript 框架八股文十问十答第七期

面试 JavaScript 框架八股文十问十答第七期 作者&#xff1a;程序员小白条&#xff0c;个人博客 相信看了本文后&#xff0c;对你的面试是有一定帮助的&#xff01;关注专栏后就能收到持续更新&#xff01; ⭐点赞⭐收藏⭐不迷路&#xff01;⭐ 1&#xff09;原型修改、重写 …

OCP使用CLI创建和构建应用

文章目录 环境登录创建project赋予查看权限部署第一个image创建route检查pod扩展应用 部署一个Python应用连接数据库创建secret加载数据并显示国家公园地图 清理参考 环境 RHEL 9.3Red Hat OpenShift Local 2.32 登录 通过 crc console --credentials 可以查看登录信息&…

Stable Video Diffusion图片转视频——Stability AI开源视频模型

我们前期介绍过Stable Diffusion&#xff0c;stable diffusion模型是Stability AI开源的一个text-to-image的扩散模型&#xff0c;其模型在速度与质量上面有了质的突破&#xff0c;玩家们可以在自己消费级GPU上面来运行此模型。 文生图大模型已经火了很长一段时间了&#xff0c…

Vue 前置导航

Vue 前置导航&#xff08;Vue Front Navigation&#xff09;是一种在 Vue.js 框架中实现导航功能的常见方式。它通常用于构建单页应用程序&#xff08;Single Page Application&#xff09;&#xff0c;通过在页面顶部或侧边栏显示导航菜单&#xff0c;使用户能够轻松切换到不同…

专业130+总分410+苏州大学837信号系统与数字逻辑考研经验电子信息与通信,真题,大纲,参考书

今年考研总分410&#xff0c;专业837信号系统与数字逻辑130&#xff0c;整体每门相对比较均衡&#xff0c;没有明显的短板&#xff0c;顺利上岸苏大&#xff0c;总结一下自己这大半年的复习经历&#xff0c;希望可以对大家有所帮助&#xff0c;也算是对自己考研做个总结。 专业…

Java:常用API接上篇 --黑马笔记

一、 StringBuilder类 StringBuilder代表可变字符串对象&#xff0c;相当于是一个容器&#xff0c;它里面的字符串是可以改变的&#xff0c;就是用来操作字符串的。 好处&#xff1a;StringBuilder比String更合适做字符串的修改操作&#xff0c;效率更高&#xff0c;代码也更…

Idea里自定义封装数据警告解决 Spring Boot Configuration Annotation Processor not configured

我们自定对象封装指定数据&#xff0c;封装类上面一个红色警告&#xff0c;虽然不影响我们的执行&#xff0c;但是有强迫症看着不舒服&#xff0c; 去除方式&#xff1a; 在pom文件加上坐标刷新 <dependency><groupId>org.springframework.boot</groupId><…

【element-ui】输入框组件el-input输入数字/输出Number类型:type=“number“、v-model.number用法

输入框组件el-input输入数字/输出Number类型 1、基础用法 输入&#xff1a;任何文本 → 输出&#xff1a;String类型 <el-input v-model"inputText"></el-input> <!-- 输入 abc —— inputText输出 "abc" 输入 123 —— inputText输出 …

JavaEE作业-实验四

目录 1.实验内容 2.实验要求 3.思路 4.程序源码 1.实验内容 简单的线上图书交易系统的服务层 2.实验要求 &#xff08;1&#xff09;使用Spring框架&#xff0c;采用XML配置 &#xff08;2&#xff09;要求具有如下服务层功能&#xff1a;商品分类、订单、购物车、库存…

11 插入排序和希尔排序

1. 插入排序 基本思想 直接插入排序是一种简单的插入排序法&#xff0c;基本思想&#xff1a; 把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中&#xff0c;直到所有的记录插入完为止&#xff0c;得到一个新的有序序列 在玩扑克牌时&#xff0c;就用…

【知识整理】招人理念、组织结构、招聘

1、个人思考 几个方面&#xff1a; 新人&#xff1a;选、育、用、留 老人&#xff1a;如何甄别&#xff1f; 团队怎么演进&#xff1f; 有没有什么注意事项 怎么做招聘&#xff1f; 2、 他人考虑 重点&#xff1a; 1、从零开始&#xff0c;讲一个搭建团队的流程 2、标…

Python pandas中read_csv函数的io参数

前言 在数据分析和处理中&#xff0c;经常需要读取外部数据源&#xff0c;例如CSV文件。Python的pandas库提供了一个强大的 read_csv() 函数&#xff0c;用于读取CSV文件并将其转换成DataFrame对象&#xff0c;方便进一步分析和处理数据。在本文中&#xff0c;将深入探讨 read…

【网页设计】春节页面背景模板

无偿下载地址&#xff1a;https://download.csdn.net/download/weixin_47040861/88811143 1.实现效果 2.代码 1.html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content&q…

go c 通过内存原始二进制内容直接传递结构体

go c 通过内存原始二进制内容直接传递结构体 传统数据传输通常通过半结构化数据(json/yaml/xml…)来交换信息。但是go 支持 二进制数据层面支持c 结构体。带来的好处就是相较于半结构化数据类型来说更快(在go中&#xff0c;解析和生成json/yaml/xml…到结构体都会经过一层反射…

【超高效!保护隐私的新方法】针对图像到图像(l2l)生成模型遗忘学习:超高效且不需要重新训练就能从生成模型中移除特定数据

针对图像到图像生成模型遗忘学习&#xff1a;超高效且不需要重新训练就能从生成模型中移除特定数据 提出背景如何在不重训练模型的情况下从I2I生成模型中移除特定数据&#xff1f; 超高效的机器遗忘方法子问题1: 如何在图像到图像&#xff08;I2I&#xff09;生成模型中进行高效…

推荐系统|召回04_离散特征处理

离散特征处理 离散特征是什么 怎么处理离散特征 One-hot编码 Embedding嵌入 从one-hot到Embedding&#xff0c;已经节省了很多的存储空间&#xff0c;但当数据量大的时候&#xff0c;还是占空间&#xff0c;所以工业界仍会对Embedding进行优化 而一个物品所对应的Embedding参数…

设计模式(前端)

设计模式 1.环境搭建 1&#xff09;初始化npm环境 下载node.js 执行npm init命令 (生成package.json) 根目录下&#xff0c;新建src文件夹&#xff0c;src文件夹下新建index.js文件&#xff1a; alert("Hello World");2&#xff09;安装webpack npm install webpa…

基于JSP的网上购书系统

点击以下链接获取源码&#xff1a; https://download.csdn.net/download/qq_64505944/88825694?spm1001.2014.3001.5503 Java项目-15 源码论文数据库配置文件 基于JSP的网上购书系统 摘要 在当今的社会中&#xff0c; 随着社会经济的快速发展以及计算机网络技术和通讯技术…

8种基本类型的包装类(与String的转换)

java针对8种基本数据类型&#xff0c;定义了相应的引用类型&#xff1a;包装类(封装类)&#xff0c;有了类的特点&#xff0c;就能调用类中的方法&#xff0c;java才是真正的面向对象。 基本数据类型 包装类byte Byteshort Shortint Integerlong Longfloat Floa…

国产光耦2024:发展机遇与挑战全面解析

随着科技的不断进步&#xff0c;国产光耦在2024年正面临着前所未有的机遇与挑战。本文将深入分析国产光耦行业的发展现状&#xff0c;揭示其在技术创新、市场需求等方面的机遇和挑战。 国产光耦技术创新的机遇&#xff1a; 国产光耦作为光电器件的重要组成部分&#xff0c;其技…