架构(十二)动态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,一经查实,立即删除!

相关文章

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…

专业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><…

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…

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

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

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

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

基于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;其技…

Python操作MySQL基础

除了使用图形化工具以外&#xff0c;我们也可以使用编程语言来执行SQL从而操作数据库。在Python中&#xff0c;使用第三方库: pymysql来完成对MySQL数据库的操作。 安装第三方库pymysql 使用命令行,进入cmd&#xff0c;输入命令pip install pymysql. 创建到MySQL的数据库连接…

CSS高级技巧

一、 精灵图 1.1 为什么需要精灵图&#xff1f; 1.2 精灵图&#xff08;sprites&#xff09;的使用 二、 字体图标 2.1 字体图标的产生 2.2 字体图标的优点 2.3 字体图标的下载 icomoom字库 http://icomoon.io 阿里iconfont字库 http://www.iconfont.cn/ 2.4 字体图标的引用…

【EAI 013】BC-Z: Zero-Shot Task Generalization with Robotic Imitation Learning

论文标题&#xff1a;BC-Z: Zero-Shot Task Generalization with Robotic Imitation Learning 论文作者&#xff1a;Eric Jang, Alex Irpan, Mohi Khansari, Daniel Kappler, Frederik Ebert, Corey Lynch, Sergey Levine, Chelsea Finn 论文原文&#xff1a;https://arxiv.org…

分享76个表单按钮JS特效,总有一款适合您

分享76个表单按钮JS特效&#xff0c;总有一款适合您 76个表单按钮JS特效下载链接&#xff1a;https://pan.baidu.com/s/1CW9aoh23UIwj9zdJGNVb5w?pwd8888 提取码&#xff1a;8888 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 学习知识费力气&#xff0c;收集…

会声会影绿幕抠图操作方法 会声会影绿幕抠图有绿色残边 绿幕抠图视频有绿边怎么处理 抖音怎么剪辑视频 视频剪辑软件推荐

科幻片里真的存在怪兽吗&#xff1f;外太空的画面是直接将演员放入太空拍摄的吗?其实这些不切实际的画面是通过绿幕拍摄实现的。你只需要在绿幕前拍一段太空漫步的视频&#xff0c;再利用会声会影的抠图功能就能实现&#xff01;如果你还不会绿幕抠图&#xff0c;我今天就手把…

《杨绛传:生活不易,保持优雅》读书摘录

目录 书简介 作者成就 书中内容摘录 良好的家世背景&#xff0c;书香门第为求学打基础 求学相关 念大学 清华研究生 自费英国留学 法国留学自学文学 战乱时期回国 当校长 当小学老师 创造话剧 支持钱锺书写《围城》 出任震旦女子文理学院的教授 接受清华大学的…