架构(十二)动态Excel

news2024/11/17 20:33:39

一、引言

        作者最近的平台项目需要生成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下载文件,并转为bytes
        byte[] 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. 取第一个Sheet
            XSSFSheet 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. 取第一个Sheet
            HSSFSheet 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.coloradmin.cn/o/1443469.html

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

相关文章

安全的接口访问策略

渗透测试 一、Token与签名 一般客户端和服务端的设计过程中&#xff0c;大部分分为有状态和无状态接口。 一般用户登录状态下&#xff0c;判断用户是否有权限或者能否请求接口&#xff0c;都是根据用户登录成功后&#xff0c;服务端授予的token进行控制的。 但并不是说有了tok…

4核8G服务器配置性能怎么样?12M带宽配置服务器能干什么?

腾讯云轻量4核8G12M轻量应用服务器支持多少人同时在线&#xff1f;通用型-4核8G-180G-2000G&#xff0c;2000GB月流量&#xff0c;系统盘为180GB SSD盘&#xff0c;12M公网带宽&#xff0c;下载速度峰值为1536KB/s&#xff0c;即1.5M/秒&#xff0c;假设网站内页平均大小为60KB…

16.1 Spring框架_SpringIoC容器与Bean管理(❤❤❤❤)

16.1 Spring框架_SpringIoC容器与Bean管理 1. Spring IOC1.1 IoC控制反转 1. Spring IOC 1.1 IoC控制反转 需要自己查找3种苹果的特色,从而选择符合自己的需求 告诉水果店老板自己的口味,由老板推荐哪种苹果,省去自己查询水果特点 在java中,各种水果就是各种对象,买水果就是创…

问题:2、计算机网络的目标是实现________。 #媒体#知识分享

问题&#xff1a;2、计算机网络的目标是实现________。 A&#xff0e;数据处理 B&#xff0e;信息传输与数据处理 C&#xff0e;资源共享与信息传输 D&#xff0e;文献查询 参考答案如图所示

【用pycharm安装第三方库时出现错误】【‘pip‘ 不是内部或外部命令,也不是可运行的程序或批处理文件。】

目录 一、问题描述 二、解决过程 1、pip的版本不是最新版本导致安装不了第三方库 2、pip最新版本安装出错 3、pip 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件” 4、 ERROR: Could not find a version that satisfies the requirement PIL (from versi…

政安晨:示例演绎机器学习中(深度学习)神经网络的数学基础——快速理解核心概念(二){两篇文章讲清楚}

这一篇与上一篇是兄弟篇&#xff0c;意在通过两篇文章讲清楚深度学习中神经网络的数学基础&#xff0c;第一次看到这篇文章的小伙伴可以从上一篇文章看起&#xff08;包括搭建环境等等都在上一篇&#xff09;&#xff0c;上一篇链接如下&#xff1a; 政安晨&#xff1a;示例演…

Flink基础篇|001_Flink是什么

&#x1f4eb; 作者简介&#xff1a;「六月暴雪飞梨花」&#xff0c;专注于研究Java&#xff0c;就职于科技型公司后端工程师 &#x1f3c6; 近期荣誉&#xff1a;华为云云享专家、阿里云专家博主、腾讯云优秀创作者 &#x1f525; 三连支持&#xff1a;欢迎 ❤️关注、&#x…

Linux系统调试课:Linux错误码介绍

文章目录 一、错误码二、错误码返回案例三、使用 goto 语句沉淀、分享、成长,让自己和他人都能有所收获!😄 📢错误代码由内核或用户空间应用程序(通过errno变量)解释。错误处理在软件开发中非常重要,而不仅仅是在内核开发中。幸运的是,内核提供的几种错误,几乎涵盖了可…

InternLM大模型实战-1.书生浦语大模型全链路开源体系

文章目录 前言笔记正文大模型成为热门关键词书生浦语开源历程从模型到应用书生浦语全链条开源开放体系数据预训练微调评测部署部署智能体LagentAgentLego 总结 前言 本系列文章是参与书生浦语全链路开源体系学习的笔记文章。B站视频教程地址&#xff1a; 笔记正文 大模型成为…

【笔记】Harmony学习:下载安装 DevEco Studio 开发工具IDE

IDE 安装 从官网下载DevEco Studio 安装包后进行安装&#xff0c; 安装完毕后&#xff0c;本地环境可能要配置相关工具&#xff0c;可以通过下面的诊断检测一下本地环境&#xff0c;通过蓝色“Set it up now” 可以快速安装。 1. Node.js (for ohpm) 2. ohpm 下载op的包管理&a…

项目学习记录

项目开发 创建项目环境配置关联git新增模块项目启动打印地址日志使用httpclient进行idea内部控制台测试使用AOP拦截器打印日志 创建项目 创建一个空项目&#xff0c;并勾选下面选项 然后进入pom.xml中修改项目配置 根据这个链接选则&#xff0c;修改项目的支持版本 链接&#…

css2复合选择器

一.后代&#xff08;包含&#xff09;选择器&#xff08;一样的标签可以用class命名以分别&#xff09; 空格表示 全部后代 应用 二.子类选择器 >表示 只要子不要孙 应用 三.并集选择器 &#xff0c;表示 代表和 一般竖着写 应用 四.伪类选择器&#xff08;包括伪链接…

【C++】类和对象(2)

这篇博客继续学习类和对象~&#xff0c;主要介绍了类的6个默认成员函数。 目录 类的6个默认成员函数 构造函数 概念 特性 析构函数 概念 特性 拷贝构造函数 特性 赋值运算符重载 运算符重载 赋值运算符重载 前置和后置重载 日期类的实现 const成员 取地址及cons…

【十三】【C++】vector简单实现

代码实现 /*vector类简单实现*/ #if 1 #define _CRT_SECURE_NO_WARNINGS#include <iostream> using namespace std; #include <vector> #include <algorithm> #include <crtdbg.h> #include <assert.h> #include <string.h>namespace MyVe…

SpringBoot循环依赖

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; SpringBoot循环依赖 循环依赖是指两个或多个模块或组件之间相互依赖形成闭环的情况。这种情况下&#xff0c;模块 A 依赖于模块 B&#xff0c;同时模块 B 也依赖于模块…

解析十六进制雷达数据格式:解析雷达数据类型。

以Cat62格式雷达数据为例&#xff0c;十六进制雷达数据部分代码&#xff1a; 3e0120bf7da4ffee0085 雷达数据使用2个字符&#xff08;1个字节&#xff09;标识&#xff0c;在这里是“3e”&#xff0c;转换为十进制数为62。 雷达数据类型父类&#xff1a; base_header_process…

openkylin(Debian系)安装nginx及安装前需要的准备

前言 现在很多linux系统都可以使用高级包管理工具安装软件了&#xff0c;但是在像是 openkylin这些新系统中&#xff0c;好多软件包虽然有&#xff0c;但是因为其依赖的包还没有做好&#xff0c;所 以安装会提示你一大堆依赖错误。所以还是要自己来编译安装咯。安装前准备&…

计算机网络之一

目录 1.因特网概述 1.1网络、互连网&#xff08;互联网&#xff09;和因特网 1.2.因特网发展的三个阶段 1.3基于ISP的三层架构的因特网 1.4.因特网的组成 2.三种交换方式 2.1电路交换 2.2分组交换 1.因特网概述 1.1网络、互连网&#xff08;互联网&#xff09;和因特网…

米贸搜|Facebook在购物季使用的Meta广告投放流程

一、账户简化 当广告系列开始投放后&#xff0c;每个广告组都会经历一个初始的“机器学习阶段”。简化账户架构可以帮助AI系统更快获得广告主所需的成效。例如&#xff1a; 每周转化次数超过50次的广告组&#xff0c;其单次购物费用要低28%&#xff1b;成功结束机器学习阶段的…

13. 串口接收模块的项目应用案例

1. 使用串口来控制LED灯工作状态 使用串口发送指令到FPGA开发板&#xff0c;来控制第7课中第4个实验的开发板上的LED灯的工作状态。 LED灯的工作状态&#xff1a;让LED灯按指定的亮灭模式亮灭&#xff0c;亮灭模式未知&#xff0c;由用户指定&#xff0c;8个变化状态为一个循…