java 利用poi根据excel模板导出数据(一)

news2024/11/25 20:30:48

前言

作为B端开发,导出数据是不可以避免的,但是有时候需求很变态,表头复杂的一笔,各种合并单元格,如下图:

 

 这些虽说用代码可以实现,但是很繁琐,而且代码并不能通用,遇到更复杂的更难受。为了追求更简单,高效率,我们需要换个方法-------利用模板导出数据!

?????能用模板?????

sure!

本文所用的excel样式,如果列更多、更复杂,直接套用就行  (下方图片中的数字是列名),行序号为1的是表格标题名称,行序号为2的是适应导出时把条件也导出来的情况,别怀疑,就是有这种需求!!

 

正文开始

1.需要用到的包

 poi

  <!-- json4excel,poi -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml-schemas</artifactId>
            <version>4.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>ooxml-schemas</artifactId>
            <version>1.3</version>
        </dependency>

nutz(工具包,非必须,公司框架,本文与之相关的就只有一个NutMap==Map<String,Object>,不用这个请手动替换)

   <!-- https://mvnrepository.com/artifact/org.nutz/nutz -->
        <dependency>
            <groupId>org.nutz</groupId>
            <artifactId>nutz</artifactId>
            <version>1.r.68.v20200427</version>
        </dependency>

2.直接上代码

  入口

    public static void doExportLongArrearsData() {
        // 要导出的数据
        NutMap nutMap = NutMap.NEW();
        nutMap.addv("comm","1111");
        nutMap.addv("a","1111");
        nutMap.addv("b","2222");
        nutMap.addv("c","11333311");
        nutMap.addv("d","1114441");
        nutMap.addv("e","555");
        nutMap.addv("f","6666");
        nutMap.addv("g","7777");
        nutMap.addv("h","88888");
        List<NutMap> list = Lists.newArrayList();
        list.add(nutMap);
        // 导出列  列数
        int colNum = 9;
        int[] colWidth = new int[colNum];
        for (int i = 0; i < colNum; i++) {
            colWidth[i] = 23;
        }
        // 从XX行开始为数据内容  excel 第一行为0
        int startRow = 5;
        //  2003版本的Excel (xls) ---- HSSFWorkbook
        //    2007版本以及更高版本 (xlsx)---- XSSFWorkbook
        XSSFWorkbook workbook = null;
        try {
            // 此处linux和windows通用   /files/cq.xlsx 在resource目录下  视情况而定
 
        /**
             *   特殊说明: this.getClass().getResourceAsStream 
             *   如果fileUrl路径前不加 / 那么会读取类文件夹下的文件。加了才会读取resource下面的文件
             *   exp: this.getClass().getResourceAsStream("/files/cq.xlsx") ==>读取resource下面的文件
             *   this.getClass().getResourceAsStream("files/cq.xlsx") ==>读取当前类下的文件
             *   源码:
             *     private String resolveName(String name) {
             *         if (name == null) {
             *             return name;
             *         }
             *         if (!name.startsWith("/")) {
             *             Class<?> c = this;
             *             while (c.isArray()) {
             *                 c = c.getComponentType();
             *             }
             *             String baseName = c.getName();
             *             int index = baseName.lastIndexOf('.');
             *             if (index != -1) {
             *                 name = baseName.substring(0, index).replace('.', '/')
             *                     +"/"+name;
             *             }
             *         } else {
             *             name = name.substring(1);
             *         }
             *         return name;
             *     }
             */
            //  InputStream inputStream = this.getClass().getResourceAsStream("/files/cq.xlsx");
            FileInputStream inputStream = new FileInputStream( new File("C:\\Users\\usaer\\Desktop\\buss.xlsx"));
            workbook = new XSSFWorkbook(inputStream);
 
            // 获取sheet
            XSSFSheet sheetAt = workbook.getSheetAt(0);
 
            // 动态列  修改表头名 、修改模板数据等操作
            // 自定义参数
            int k = 1;
            updateCellLoad(workbook,sheetAt,k);
 
            // 填充数据
            fillBodyData( sheetAt,startRow,list,colWidth);
 
            // 设置单元格宽度 (不设置的话,就是模板的宽度)
            if (null != colWidth) {
                for (int i = 0; i < colWidth.length; i++) {
                    sheetAt.setColumnWidth(i, colWidth[i] * 256 + 184);
                }
            }
 
            // 输出流
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            // excel工作空间写入流
            workbook.write(byteArrayOutputStream);
            InputStream wrap = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
            // 写到本地
            writeToLocal("C:\\Users\\usaer\\Desktop\\buss_"+k+".xlsx",wrap);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        } finally {
            // 关闭流
            if (null != workbook)
                try {
                    workbook.close();
                }
                catch (IOException e) {
 
                }
        }
    }

修改列的方法 (填充数据之前之后都可以修改内容,包括但不限于修改列、修改行、样式等)

 
    public static void updateCellLoad( XSSFWorkbook workbook,Sheet sheet,int k) {
        try {
            //设置行 指定行 
            Row titlerow=sheet.getRow(2);
            //根据索引获取对应的列   如果合并单元格 有可能第一列就找不到  根据需求修改参数值
            Cell cell=titlerow.getCell(0, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
            //设置列的类型是字符串
            cell.setCellType(CellType.STRING);
            cell.setCellValue(k==1?"1":"0");
            String titleValue=cell.getStringCellValue();
            System.out.println(titleValue);
 
 
            // 设置行 指定行修改数据和样式 有可能第一行就找不到  根据需求修改参数值
            CellStyle style1 = workbook.createCellStyle();
            // 自动换行
            style1.setWrapText(true);
            Row row1 = sheet.createRow(1);
            // 行高度
            row1.setHeight(Short.parseShort("1000"));
            Cell row1Cell = row1.createCell(0, CellType.STRING);
            row1Cell.setCellStyle(style1);
            StringBuilder sb = new StringBuilder();
            sb.append("条件1:").append("ABC").append("\r\n");
            sb.append("条件2:").append("DEF").append("\r\n");
            sb.append("条件3:").append("GHI").append("\r\n");
            row1Cell.setCellValue(sb.toString());
 
        } catch (EncryptedDocumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
 
    }

填充数据的方法

private static void fillBodyData(Sheet sheet, int startRow, List<NutMap> bodyList, int[] colWidth) {
 
        // 碰到 时间、金额等需要转换的  在setCellValue 里面操作
        for (int rowNum = 0; rowNum < bodyList.size(); rowNum++) {
            NutMap map = bodyList.get(rowNum);
            Row row = sheet.createRow(rowNum + startRow);
            int colNum = 0;
            // comm
            Cell accountNumberCell = row.createCell(colNum++, CellType.STRING);
            String accountNumber = map.getString("comm");
            colWidth[colNum - 1] = Math.max(colWidth[colNum - 1], Strings.charLength(accountNumber));
            accountNumberCell.setCellValue(accountNumber);
            // a
            Cell accountNameCell = row.createCell(colNum++, CellType.STRING);
            String accountName = map.getString("a");
            colWidth[colNum - 1] = Math.max(colWidth[colNum - 1], Strings.charLength(accountName));
            accountNameCell.setCellValue(accountName);
            // b
            Cell mobileCell = row.createCell(colNum++, CellType.STRING);
            String mobile = map.getString("b");
            colWidth[colNum - 1] = Math.max(colWidth[colNum - 1], Strings.charLength(mobile));
            mobileCell.setCellValue(mobile);
 
            // c
            Cell addressCell = row.createCell(colNum++, CellType.STRING);
            String address = map.getString("c");
            colWidth[colNum - 1] = Math.max(colWidth[colNum - 1], Strings.charLength(address));
            addressCell.setCellValue(address);
 
            // d
            Cell meterNoCell = row.createCell(colNum++, CellType.STRING);
            String meterNo = map.getString("d");
            meterNoCell.setCellValue(meterNo);
            colWidth[colNum - 1] = Math.max(colWidth[colNum - 1], Strings.charLength(meterNo));
 
            // e
            Cell uncollectedNumberCell = row.createCell(colNum++, CellType.STRING);
            String uncollectedNumber = map.getString("e");
            uncollectedNumberCell.setCellValue(uncollectedNumber);
            colWidth[colNum - 1] = Math.max(colWidth[colNum - 1], Strings.charLength(uncollectedNumber));
 
            // f
            Cell uncollectedDosageCell = row.createCell(colNum++, CellType.STRING);
            String uncollectedDosage = map.getString("f");
            uncollectedDosageCell.setCellValue(uncollectedDosage);
            colWidth[colNum - 1] = Math.max(colWidth[colNum - 1], Strings.charLength(uncollectedDosage));
 
            // g
            Cell uncollectedDosageCell1 = row.createCell(colNum++, CellType.STRING);
            String uncollectedDosage1 = map.getString("g");
            uncollectedDosageCell1.setCellValue(uncollectedDosage1);
            colWidth[colNum - 1] = Math.max(colWidth[colNum - 1], Strings.charLength(uncollectedDosage1));
 
            // h
            Cell uncollectedDosageCell2 = row.createCell(colNum++, CellType.STRING);
            String uncollectedDosage2 = map.getString("h");
            uncollectedDosageCell2.setCellValue(uncollectedDosage2);
            colWidth[colNum - 1] = Math.max(colWidth[colNum - 1], Strings.charLength(uncollectedDosage2));
        }
    }

写到本地

 private static void writeToLocal(String destination, InputStream input)
            throws IOException {
        int index = 0;
        byte[] bytes = new byte[1024];
        FileOutputStream downloadFile = new FileOutputStream(destination);
        while ((index = input.read(bytes)) > 0) {
            downloadFile.write(bytes, 0, index);
        }
        downloadFile.close();
        input.close();
    }

代码就这些,纯测试,有些命名很随意,到时候用的时候自己改

效果图

注意: 

本文读取文件是从本地读取的,部署后,需要把模板文件放在resource下,利用代码中

 // 此处linux和windows通用   /files/cq.xlsx 在resource目录下  视情况而定
           //  InputStream inputStream = this.getClass().getResourceAsStream("/files/cq.xlsx");

来读取(本人框架非spring,spring自行解决),总之,获取到模板文件就是成功!!

如果想要设置单元格的样式比如字体垂直、水平居中等可以在  fillBodyData 中添加如下代码

 // 单元格样式
        XSSFCellStyle cellStyle = workbook.createCellStyle();
        // 设置单元格的水平对齐类型
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        // 设置单元格的垂直对齐类型
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);

 然后再循环体内每个cell设置style ,例如:

 Cell accountNumberCell = row.createCell(colNum++, CellType.STRING);
            String accountNumber = map.getString("comm");
            colWidth[colNum - 1] = Math.max(colWidth[colNum - 1], Strings.charLength(accountNumber));
            accountNumberCell.setCellValue(accountNumber);
// 设置style
            accountNumberCell.setCellStyle(cellStyle);

 附加,如果springboot项目,获取项目目录下的模板,和直接输出到客户端,可参考:

@GetMapping("/exportTest4")
    //@ResponseBody
    public void exportTest4(HttpServletResponse response) {
//设置信息头,告诉浏览器内容为excel类型
        response.setHeader("content-Type", "application/vnd.ms-excel");
        //设置下载名称
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("展商模板.xlsx", StandardCharsets.UTF_8.name()));

//获取项目resources/excel目录下的excel模板文件
String REPORT_MONTH = ClassUtils.getDefaultClassLoader().getResource("").getPath() + "/excel/test2.xlsx";
        String path = REPORT_MONTH;
        try{
            System.out.println("path1=="+path);
            path = path.substring(1,path.length());
            path = java.net.URLDecoder.decode(path,"utf-8");
            System.out.println("path2=="+path);
        }catch (Exception e){
            e.printStackTrace();
        }

        String filename = "D://核酸检测信息.xls";
        try {
            // 输出到硬盘,所以要用到IO流,输出到fileName指定的路径上
            FileOutputStream fos = new FileOutputStream(filename);
            wb.write(fos);//写出
            wb.close();//关闭
            System.out.println("导出成功");
        } catch (Exception e) {
            //异常信息
            e.printStackTrace();
        }
}

如果是若依系统,也可以使用若依的通用下载方式下载:

直接返回文件名称,然后再页面通过ajax再次请求

return success(filename);

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

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

相关文章

哈工大华为提出ControlVideo:一种无需训练的可控视频生成方法

点击下方卡片&#xff0c;关注“CVer”公众号 AI/CV重磅干货&#xff0c;第一时间送达 点击进入—>【扩散模型和Transformer】交流群 导读 哈工大&华为云最新提出了一种可控的文本-视频生成方法ControlVideo&#xff0c;在无需训练的条件下&#xff0c;仅使用一张2080Ti…

长沙之行第二天

这是学习笔记的第 2458篇文章 已经从长沙返京一个多星期了&#xff0c;旅行日记还没有写好&#xff0c;真是羞愧&#xff0c;赶紧补一补回忆。 整体来看返京后这一周我最大的变化就是几乎每天都订1次外卖吃长沙牛肉粉。 第二天 自第一天逛完橘子洲之后&#xff0c;我们的行程重…

3.11 Ext JS文件上传基本使用

文件上传对应的组件是Ext.form.field.File。 组件的效果是输入框+文件选择按钮,如下图所示: 点击“选择文件的按钮”, 会弹出操作系统选择文件的对话框,如下图所示窗口: 选择文件后,输入框会根据不同的浏览器有不同的显示, 有的浏览器是文件名,有的浏览器是完整路径,…

c#特性Attribute

C# 特性&#xff08;Attribute&#xff09; 特性&#xff08;Attribute&#xff09;是用于在运行时传递程序中各种元素&#xff08;比如类、方法、结构、枚举、组件等&#xff09;的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在…

分享几款还不错的工具,这几个工具你们知道吗?

1、可口的披萨 这是一款非常有趣的小游戏&#xff0c;它不仅可以帮助你超解压&#xff0c;还能消磨时间。你将扮演一位店主&#xff0c;经营一家小店。在这个过程中&#xff0c;你会遇到各种不同的人&#xff0c;每个人都有着自己的故事和背景。这些故事非常感人&#xff0c;会…

scitb5函数1.6版本(交互效应函数P for interaction)尝鲜版发布----用于一键生成交互效应表

在SCI文章中&#xff0c;交互效应表格&#xff08;通常是表五&#xff09;几乎是高分SCI必有。因为增加了亚组人群分析&#xff0c;增加了文章的可信度&#xff0c;能为文章锦上添花&#xff0c;增加文章的信服力&#xff0c;还能进行数据挖掘。 在上一个版本中&#xff0c;我们…

使用PlotNeuralNet绘制深度学习网络图的基本操作

使用PlotNeuralNet绘制深度学习网络图的基本操作 PlotNeuralNet工具&#xff0c;具如其名&#xff0c;plot neural net用的&#xff0c;首先我们看看效果&#xff1a; PlotNeuralNet安装与简单命令了解 关于如何安装大家可以参考网上的其他教程&#xff0c;网上有很多教程&…

如何使用ArcGIS查找离家最近的地铁站(附练习数据)

学习GIS的目的除了可以用在工作上之外&#xff0c;还可以用在平时的生活中&#xff0c;比如可以用来查找定位离家最近的地铁站&#xff0c;这里给大家介绍一下查找方法&#xff0c;希望能够对大家有所帮助。 近邻分析 在ArcToolbox中点击“分析工具\邻域分析\近邻分析”&#…

数据体系建设-ODS|DW|TDM|ADS介绍

参考书目《数据中台&#xff1a;让数据用起来》 ODS&#xff1a;各业务生成的基础数据存表&#xff0c;如log日志数据等DW&#xff1a;在ods基础上&#xff0c;分主题整合数据TDM&#xff1a;存储标签数据ADS&#xff1a;基于上面的数据源整合而成的供业务应用的指标报表等 贴…

什么是EDI 858装运信息?

EDI 858是电子数据交换&#xff08;Electronic Data Interchange&#xff0c;简称EDI&#xff09;中的一种标准格式&#xff0c;它主要用于在供应链管理中进行物流和运输的数据交换。EDI 858是指基于ASC X12标准的858交付和接收数据集&#xff0c;也被称为”Shipping Notice/Ma…

基于Spring boot的图书商城管理系统-源码、数据库、LW

框架&#xff1a;Springboot 数据库&#xff1a;MySQL 下载链接&#xff1a; https://download.csdn.net/download/yw1990128/87851197 B站运行链接&#xff1a; 基于Springboot的图书商城管理系统_哔哩哔哩_bilibili 引言 项目开发背景 Internet最早在美国出现&#xf…

GLSurfaceView 与 SurfaceView 联系与区别

同系列文章&#xff1a; SurfaceView为什么不能做动画&#xff1f; SurfaceViewMediaPlayer实现视频播放 本文目录 笔试题解析有了SurfaceView&#xff0c;为什么还要提供一个GLSurfaceView?SurfaceView有哪些用途&#xff1f; 笔试题解析 下面是属于GLSurfaceView特性的是&a…

Android开机流程、Zygote进程、systemserver、ServiceManager

一、Loader层 1、Boot ROM: 上电后&#xff0c;BootRom会被激活&#xff0c;引导芯片代码开始从预定义的地方&#xff08;固化在ROM&#xff09;开始执行&#xff0c;然后加载引导程序到RAM。 2、 Boot Loader引导程序 Android是基于Linux系统的&#xff0c;它没有BI…

Arcgis进阶篇(6)——如何将Arcgis Pro的离线数据发布成服务

常常因为Arcgis Server&#xff08;或者GeoScene Server&#xff09;昂贵的价格&#xff0c;而导致小项目技术选型选择开源的GIS Server&#xff08;如GeoServer等&#xff09;。但用完之后&#xff0c;发现后者实在拉跨&#xff0c;使用对比差异巨大。那就只能另想办法&#x…

商家618活动文案怎么写,注意事项

马上又是一年的618&#xff0c;特别是最近大家消费热情高涨的时候。今天&#xff0c;就来和大家好好唠一唠618。商家618活动文案怎么写&#xff0c;以及注意事项。 为了以防有些人没有真正了解618&#xff0c;虽然可能性很小&#xff0c;但是本着严谨的原则&#xff0c;还是先介…

MySQL数据库 3.内部数据模型

&#x1f914;前言&#xff1a; &#x1f642;关系型数据库&#xff1a; 关系型数据库是建立在关系模型基础上&#xff0c;由多张相互连接的二维表组成的数据库。 关系型数据库是一种使用关系模型来组织数据的数据库系统。数据以二维表的形式存储&#xff0c;一张表代表一种数…

chatgpt赋能python:Python交流微信群:打开学习和分享的大门

Python交流微信群&#xff1a;打开学习和分享的大门 Python是一种高级编程语言&#xff0c;得益于其灵活性&#xff0c;易学性和强大的社区支持&#xff0c;已经成为许多程序员首选的开发语言。但是&#xff0c;学习Python并没有那么容易&#xff0c;很多人可能会面临困惑和挫…

FPGA 设计中经常犯的 10 个错误

本文列出了FPGA设计中常见的十个错误。我们收集了 FPGA 工程师在其设计中犯的 10 个最常见错误&#xff0c;并提供了解决方案的建议和替代方案。本文假定读者已经具备 RTL 设计和数字电路方面的基础。接下来让我们深入探讨在FPGA 设计中要避免的 10 大错误。 异步逻辑 FPGA 设计…

C# webAPI 精解

入门 创建一个web项目 先创建一个web项目 基本可以运行的程度 用postman进行接口测试 .NET Framework 和 .NET Core 都可以创建 webAPI 这里用 .NET Framework 比较简单 。 启动 Visual Studio&#xff0c;并从“开始”页中选择“新建项目”。 或者&#xff0c;在 “文件” 菜…

简单介绍Roop(类似SimSwap)单张图视频换脸的项目

文章目录 &#xff08;一&#xff09;关于Roop&#xff08;二&#xff09;安装Roop&#xff08;CPU&#xff09;&#xff08;2.1&#xff09;克隆仓库&#xff08;2.2&#xff09;设置python国内源&#xff08;2.2&#xff09;检查安装virtualenv&#xff08;2.3&#xff09;创…