POI基于Excel模板导出数据

news2024/12/27 15:46:34

1、基于模板导出列表数据

1.1、需求

注意:使用附件的形式下载,前端访问必须通过window.open(),否则附件可能无法下载。

按照以下样式导出excel
在这里插入图片描述

1.2、思路

首先准备一个excel模板,这个模板把复杂的样式和固定的内容先准备好并且放入到项目中,然后读取到模板后向里面放入数据。

1.3、实现

第一步:准备一个excel作为导出的模板,模板内容如下

第一个sheet:

在这里插入图片描述

第二个sheet:

在这里插入图片描述

第二步:把这个模板改一个英文名称比如:userList.xlsx,放入到项目中
如果要部署在云服务器上,要把这个文件存储到jar包外,否则访问不到。

在这里插入图片描述

第三步:UserService实现方法

public void downLoadXlsxWithTempalte(HttpServletRequest request, HttpServletResponse response) throws Exception {
    //        获取模板的路径
    File rootPath = new File(ResourceUtils.getURL("classpath:").getPath()); //SpringBoot项目获取根目录的方式
    File templatePath = new File(rootPath.getAbsolutePath(),"/excel_template/userList.xlsx");
    //        读取模板文件产生workbook对象,这个workbook是一个有内容的工作薄
    Workbook workbook  = new XSSFWorkbook(templatePath);
    //        读取工作薄的第一个工作表,向工作表中放数据
    Sheet sheet = workbook.getSheetAt(0);
    //        获取第二个的sheet中那个单元格中的单元格样式
    CellStyle cellStyle = workbook.getSheetAt(1).getRow(0).getCell(0).getCellStyle();
    //        处理内容
    List<User> userList = this.findAll();
    int rowIndex = 2;
    Row row = null;
    Cell cell = null;
    for (User user : userList) {
        row = sheet.createRow(rowIndex);
        row.setHeightInPoints(15); //设置行高

        cell = row.createCell(0);
        cell.setCellValue(user.getId());
        cell.setCellStyle(cellStyle); //设置单元格样式

        cell = row.createCell(1);
        cell.setCellValue(user.getUserName());
        cell.setCellStyle(cellStyle);

        cell = row.createCell(2);
        cell.setCellValue(user.getPhone());
        cell.setCellStyle(cellStyle);

        cell = row.createCell(3);
        cell.setCellValue(simpleDateFormat.format(user.getHireDate()));
        cell.setCellStyle(cellStyle);

        cell = row.createCell(4);
        cell.setCellValue(user.getAddress());
        cell.setCellStyle(cellStyle);

        rowIndex++;
    }
    workbook.removeSheetAt(1);
    //            导出的文件名称
    String filename="用户列表数据.xlsx";
    //            设置文件的打开方式和mime类型
    ServletOutputStream outputStream = response.getOutputStream();
    response.setHeader( "Content-Disposition", "attachment;filename="  + new String(filename.getBytes(),"ISO8859-1"));
    response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
    workbook.write(outputStream);

}

第四步:修改UserController中的方法,导出测试

@GetMapping(value = "/downLoadXlsxByPoi",name = "使用POI下载高版本")
public void downLoadXlsx(HttpServletRequest request,HttpServletResponse response) throws Exception{
    //       userService.downLoadXlsx(response);
    userService.downLoadXlsxWithTempalte(request,response); //下载的excel带样式
}

2、导出用户详细数据

2.1、 需求

如下,点击用户列表中的下载按钮,下载文件内容如下
在这里插入图片描述

2.2、思路

最简单的方式就是先根据案例制作模板,导出时查询用户数据、读取模板,把数据放入到模板中对应的单元格中,其中我们先处理最基本的数据,稍后再处理图片

2.3、实现

第一步:制作一个excel导出模板,如下

在这里插入图片描述

第二步:制作好的模板放入到项目中
在这里插入图片描述

第三步:Controller中添加方法

@GetMapping(value = "/download",name = "导出用户详细信息")
public void downLoadUserInfoWithTempalte(Long id,HttpServletRequest request,HttpServletResponse response) throws Exception{
    userService.downLoadUserInfoWithTempalte(id,request,response);
}

第四步:在UserService中添加方法

    public void downLoadUserInfoWithTempalte(Long id, HttpServletRequest request, HttpServletResponse response) throws Exception  {
        //        获取模板的路径
        File rootPath = new File(ResourceUtils.getURL("classpath:").getPath()); //SpringBoot项目获取根目录的方式
        File templatePath = new File(rootPath.getAbsolutePath(),"/excel_template/userInfo.xlsx");
//        读取模板文件产生workbook对象,这个workbook是一个有内容的工作薄
        Workbook workbook  = new XSSFWorkbook(templatePath);
//        读取工作薄的第一个工作表,向工作表中放数据
        Sheet sheet = workbook.getSheetAt(0);
//        处理内容
        User user = userMapper.selectByPrimaryKey(id);
//        接下来向模板中单元格中放数据
//        用户名   第2行第2列
        sheet.getRow(1).getCell(1).setCellValue(user.getUserName());
//        手机号   第3行第2列
        sheet.getRow(2).getCell(1).setCellValue(user.getPhone());
//        生日     第4行第2列  日期转成字符串
        sheet.getRow(3).getCell(1).setCellValue
            (simpleDateFormat.format(user.getBirthday()));
//        工资 第5行第2列
        sheet.getRow(4).getCell(1).setCellValue(user.getSalary());
//        工资 第6行第2列
        sheet.getRow(5).getCell(1).setCellValue
            (simpleDateFormat.format(user.getHireDate()));
//        省份     第7行第2列
        sheet.getRow(6).getCell(1).setCellValue(user.getProvince());
//        现住址   第8行第2列
        sheet.getRow(7).getCell(1).setCellValue(user.getAddress());
//        司龄     第6行第4列暂时先不考虑
          
//        城市     第7行第4列
        sheet.getRow(6).getCell(3).setCellValue(user.getCity());

//            导出的文件名称
        String filename="用户详细信息数据.xlsx";
//            设置文件的打开方式和mime类型
        ServletOutputStream outputStream = response.getOutputStream();
        response.setHeader( "Content-Disposition", "attachment;filename="  + new String(filename.getBytes(),"ISO8859-1"));
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        workbook.write(outputStream);
    }

点击页面上的下载按钮,效果如下:
在这里插入图片描述

接下来处理一下头像照片和司龄

3、导出数据带图片、公式

3.1、导出图片

个人信息的导出中包含了头像照片,需要用到POI的导出图片功能,那么POI主要提供了两个类来处理照片,这两个类是Patriarch和ClientAnchor前者负责在表中创建图片,后者负责设置图片的大小位置。

在UserService的方法中添加以下代码

// 先创建一个字节输出流
ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
// BufferedImage是一个带缓冲区图像类,主要作用是将一幅图片加载到内存中
BufferedImage bufferImg = ImageIO.read(new File(rootPath + user.getPhoto()));
// 把读取到图像放入到输出流中
ImageIO.write(bufferImg, "jpg", byteArrayOut);
// 创建一个绘图控制类,负责画图
Drawing patriarch = sheet.createDrawingPatriarch();
// 指定把图片放到哪个位置
ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, 2, 1, 4, 5);
// 开始把图片写入到sheet指定的位置
patriarch.createPicture(anchor, workbook.addPicture(
    byteArrayOut.toByteArray(), Workbook.PICTURE_TYPE_JPEG));

关于XSSFClientAnchor的8个参数说明:

#36000表示1厘米
dx1 - the x coordinate within the first cell.//定义了图片在第一个cell内的偏移x坐标,既左上角所在cell的偏移x坐标,一般可设0
dy1 - the y coordinate within the first cell.//定义了图片在第一个cell的偏移y坐标,既左上角所在cell的偏移y坐标,一般可设0
dx2 - the x coordinate within the second cell.//定义了图片在第二个cell的偏移x坐标,既右下角所在cell的偏移x坐标,一般可设0
dy2 - the y coordinate within the second cell.//定义了图片在第二个cell的偏移y坐标,既右下角所在cell的偏移y坐标,一般可设0

col1 - the column (0 based) of the first cell.//第一个cell所在列,既图片左上角所在列
row1 - the row (0 based) of the first cell.//图片左上角所在行
col2 - the column (0 based) of the second cell.//图片右下角所在列
row2 - the row (0 based) of the second cell.//图片右下角所在行

3.2、导出公式

应用场景说明,在导出用户详细数据时有一个司龄的显示,这里的司龄就是截止到现在入职到本公司的时间,为了学习POI对公式的操作,我们这里使用POI的公式来做。

计算截止到现在入职到本公司的时间应该用到两个日期相差的函数:DATEDIF函数,这个函数需要3个参数

P1: 一个日期 P2:截止日期 P3: 时间单位 举例:

1、DATEDIF(“2015-10-01”,“2020-10-01”,“y”) 结果是5

2、CONCATENATE(DATEDIF(“2015-10-01”,“2020-10-01”,“y”)),“年”,DATEDIF(“2015-10-01”,“2020-10-01”,“ym”),“个月”) 结果是5年0个月

放到这个用户导出时,第一个参数就是放到相应单元格上数据,第二个参数就是当天时间,

如果直接在excel中操作,如下:
在这里插入图片描述

在使用POI导出时使用setCellFormula方法来设置公式:
在这里插入图片描述

关于POI支持公式详见官网: https://poi.apache.org/components/spreadsheet/eval-devguide.html

ps:其实在正常开发时应该在模板中直接设置好公式,这样打开直接导出的excel文档时公式会直接运行出我们想要的结果

若模板已经设置好了计算司龄的公司,则不需要进行代码编写计算司龄。因为在模板导出后Excel自动帮我们计算了。

4、自定义导出详细数据的引擎

4.1、说明

好处:通过ExcelExportEngine引擎可以实现只关注模板,即使修改了模板,代码也不需要改动,更加便捷。

看我们刚才导出时写的代码,必须要提前知道要导出数据在哪一行哪一个单元格,但是如果模板一旦发生调整,那么我们的java代码必须要修改,我们可以自定义个导出的引擎,有了这个引擎即使模板修改了我们的java代码也不用修改
在这里插入图片描述

4.2、思路

在制作模板时,在需要插入数据的位置我们坐上标记,在导出时,对象的属性要和标记做对应,如果对应匹配一样,就把值赋值到相应的位置。

4.3、实现

第一步:制作模板,命名 userInfo2.xlsx

实现方式就是单元格中标记为的实体类的属性,在遍历的时候匹配到实体类对应的属性时填充到相应的单元格中。

在这里插入图片描述
第二步:添加到项目中
在这里插入图片描述

第三步:实现导出的引擎代码

package com.itheima.utils;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.tomcat.util.http.fileupload.ByteArrayOutputStream;
import org.springframework.util.ResourceUtils;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

public class ExcelExportEngine {

    private static SimpleDateFormat  sdf = new SimpleDateFormat("yyyy-MM-dd");

    public  static Workbook writeToExcel(Object object, Workbook workbook,String photoPath) throws Exception{
      //先把bean转成map,这个Object表示对象,也就是bean
        Map<String, Object> map = EntityUtils.entityToMap(object);
        //循环遍历每一对数据,把日期型的转成字符串,方便导出
        for (String key : map.keySet()) {
            Object vlaue = map.get(key);
            if(vlaue instanceof Date){
                System.out.println(sdf.format(vlaue));
                map.put(key,sdf.format(vlaue));
            }
        }
        //获取第一个sheet,整体的思路是循环100个行的100个单元格
        Sheet sheet = workbook.getSheetAt(0);
        Cell cell =null;
        Row row = null;
        for (int i = 0; i < 100; i++) {
            row = sheet.getRow(i); //获取到空行为止
            if(row==null){
                break;
            }else{
                for (int j = 0; j < 100; j++) {
                    cell = row.getCell(j);//获取到空单元格不处理
                    if(cell!=null){
                        writeCell(cell,map); //开始向单元格中写内容
                    }
                }
            }
        }

        if(StringUtils.isNotBlank(photoPath)){
            File rootPath = new File(ResourceUtils.getURL("classpath:").getPath()); //SpringBoot项目获取根目录的方式
            ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
//        BufferedImage是一个带缓冲区图像类,主要作用是将一幅图片加载到内存中
            BufferedImage bufferImg = ImageIO
                    .read(new File(rootPath + photoPath));
            ImageIO.write(bufferImg, "jpg", byteArrayOut);
            Drawing patriarch = sheet.createDrawingPatriarch();

            Sheet sheet2 = workbook.getSheetAt(1);
            row = sheet2.getRow(0);
            int col1 = ((Double) row.getCell(0).getNumericCellValue()).intValue();
            int row1 = ((Double) row.getCell(1).getNumericCellValue()).intValue();
            int col2 = ((Double) row.getCell(2).getNumericCellValue()).intValue();
            int row2 = ((Double) row.getCell(3).getNumericCellValue()).intValue();
//            锚点,固定点
            ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0,  col1, row1, col2, row2);
            patriarch.createPicture(anchor, workbook.addPicture(byteArrayOut.toByteArray(), Workbook.PICTURE_TYPE_JPEG));
            workbook.removeSheetAt(1);
        }

        return workbook;
    }

    private static void writeCell(Cell cell, Map<String, Object> map) {
        CellType cellType = cell.getCellType();
        switch (cellType){
            case FORMULA:{  //如果是公式就直接放行了
                break;
            }default:{
                String cellValue = cell.getStringCellValue();
                //就是判断一下获取到单元格中的值是否和map中的key保持一致
                if(StringUtils.isNotBlank(cellValue)){
                    for (String key : map.keySet()) {
                        if(key.equals(cellValue)){
                            cell.setCellValue(map.get(key).toString());
                        }
                    }
                }
            }
        }
    }
}

第四步:修改UserService的方法

public void downLoadUserInfoWithTempalte2(Long id, HttpServletRequest request, HttpServletResponse response) throws Exception  {
    //        获取模板的路径
    File rootPath = new File(ResourceUtils.getURL("classpath:").getPath()); //SpringBoot项目获取根目录的方式
    File templatePath = new File(rootPath.getAbsolutePath(),"/excel_template/userInfo2.xlsx");
    //        读取模板文件产生workbook对象,这个workbook是一个有内容的工作薄
    Workbook workbook  = new XSSFWorkbook(templatePath);
    // 查询用户信息
    User user = userMapper.selectByPrimaryKey(id);
   // 这里使用引擎直接导出
    workbook = ExcelExportEngine.writeToExcel(user,workbook,user.getPhoto());
    //            导出的文件名称
    String filename="用户详细信息数据.xlsx";
    //            设置文件的打开方式和mime类型
    ServletOutputStream outputStream = response.getOutputStream();
    response.setHeader( "Content-Disposition", "attachment;filename="  + new String(filename.getBytes(),"ISO8859-1"));
    response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
    workbook.write(outputStream);
}

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

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

相关文章

2023年了,java后端还有未来吗?

前言 Java当下确实是比较的内卷&#xff0c;但关键在于个人&#xff0c;可以看看不同地方&#xff08;这里主要举例北上广深一线城市&#xff09;对于Java开发工程师这个职位的具体要求&#xff1a; 在以下北上广深这些一线大城市的面试招聘当中不难看出&#xff0c;凡是工资…

搭建HTTPS服务器

HTTPS代理服务器的作用与价值 HTTPS代理服务器可以帮助我们实现网络流量的转发和加密&#xff0c;提高网络安全性和隐私保护。本文将指导您从零开始搭建自己的HTTPS代理服务器&#xff0c;让您更自由、安全地访问互联网。 1. 准备工作&#xff1a;选择服务器与操作系统 a. 选…

SpringMVC框架@RequestMapping用法,处理器方法参数接收,处理器方法返回值详解

1. RequestMapping 定义请求规则 1.1 指定模块名称 通过RequestMapping 注解可以定义处理器对于请求的映射规则。该注解可以注解在方 法上&#xff0c;也可以注解在类上&#xff0c;但意义是不同的。value 属性值常以“/”开始。RequestMapping 的 value 属性用于定义所匹配请…

麒麟信安参展第二十五届中国国际软件博览会,荣膺2022-2023年度优秀创新软件产品奖

8月31日至9月2日&#xff0c;第二十五届中国国际软件博览会&#xff08;以下简称&#xff1a;软博会&#xff09;在天津梅江会展中心举办。软博会是我国软件和信息技术服务领域规模最大、持续时间最长、最具影响力的专业盛会&#xff0c;由中国电子信息行业联合会、天津市工业和…

Medium: Where to Define Qualified users in A/B testing?

1. Common AB Testing Setup Issue (Framework) 局限性: unqualified users will also be considered and mess up experimentation results.

国产光刻机或已量产,ASML立即抛弃美国,中国芯片迈上新台阶

国产5G手机的推出&#xff0c;预示着国产的先进光刻机已投产&#xff0c;恰在此时ASML突然转变态度&#xff0c;表示已获得荷兰的许可&#xff0c;将对中国出售更先进的2000i光刻机&#xff0c;反过来印证着中国先进光刻机已成功量产。 这几年ASML的态度可谓多变&#xff0c;早…

数据库管理系统(DBMS)的事务四大特性(ACID)以及事务的四种隔离级别

一、什么是ACID&#xff1f; ACID是原子性&#xff08;Atomicity&#xff09;、一致性&#xff08;Consistency&#xff09;、隔离性&#xff08;Isolation&#xff09;和持久性&#xff08;Durability&#xff09; 的缩写&#xff0c;是在可靠数据库管理系统&#xff08;DBMS&…

苹果微信聊天记录删除了怎么恢复?果粉原来是这样恢复的

粗心大意删除了微信聊天记录&#xff1f;有时候&#xff0c;一些小伙伴可能只是想要删除一部分聊天记录&#xff0c;但是在进行批量删除时&#xff0c;不小心勾选到了很重要的对话&#xff0c;从而导致记录丢失。 如果这时想找回聊天记录该怎么办&#xff1f;微信聊天记录删除…

185页智慧校园顶层架构及建设方案 WORD

本资料来源公开网络&#xff0c;仅供个人学习&#xff0c;请勿商用&#xff0c;如有侵权请联系删除&#xff0c;更多浏览公众号&#xff1a;智慧方案文库。目录 一、 项目概述 1.1 建设背景 1.2 建设现状 1.3 总体目标 二、 总体规划设计 2.1 智慧校园网系统 2.1.…

Python程序设计基础:异常

文章目录 一、异常的类型二、异常的处理过程 一、异常的类型 在使用Python进行程序编写时&#xff0c;很容易就遇到程序出错的情况&#xff0c;可能是引用了未定义的变量&#xff0c;也可能是访问了字典中不存在的键&#xff0c;还可能是读文件时&#xff0c;只读模式下读取了…

【Sentinel】降级源码:插槽DegradeSlot与断路器的实现

文章目录 1、实现原理2、DegradeSlot类3、CircuitBreaker4、触发断路器 1、实现原理 Sentinel的降级是基于状态机来实现的&#xff1a; 2、DegradeSlot类 熔断降级的逻辑在DegradeSlot类中实现&#xff0c;核心API&#xff1a; Override public void entry(Context context,…

数字化企业可能会用到哪些系统?

数字企业依靠各种软件系统来驱动其运营、增强客户体验并实现其业务目标。这些系统可能会根据企业、行业的性质和具体目标的不同而有很大差异。以下是数字化企业使用的一些常见的软件系统类型&#xff1a; 1.企业资源规划 (ERP) 系统&#xff1a; ERP 系统集成了财务、人力资源…

【Dots之009】WriteGroup

一、WriteGroup基础知识 WriteGroup是一个属性标签&#xff0c;在ECS的代码中可以用于修饰组件&#xff1b; 格式如下&#xff1a; [WriteGroup(typeof(T))]那么接下来看几段代码进行理解&#xff1b; 1、定义几个组件 struct ScaleComponentData : IComponentData {publi…

日流量200亿,携程网关的架构设计

说在前面 在40岁老架构师 尼恩的读者社区(50)中&#xff0c;很多小伙伴拿到一线互联网企业如阿里、网易、有赞、希音、百度、滴滴的面试资格。 就在昨天&#xff0c;尼恩指导一个小伙伴简历&#xff0c;写了一个《高并发 Netty网关项目》&#xff0c;此项目帮这个小伙拿到 字…

电脑c盘变红满了怎么清理?4个方法轻松清理!

“我的电脑才用了不到一年&#xff0c;现在就已经满了&#xff01;电脑c盘变红满了应该怎么清理呢&#xff1f;有什么方法能帮我清理的干净一点吗&#xff1f;希望大家给我出出主意。” 随着我们使用电脑时间的增多&#xff0c;电脑C盘可能会变得满满当当&#xff0c;这会不仅会…

失效的访问控制及漏洞复现

失效的访问控制(越权) 1. 失效的访问控制(越权) 1.1 OWASP TOP10 1.1.1 A5:2017-Broken Access Control 未对通过身份验证的用户实施恰当的访问控制。攻击者可以利用这些缺陷访问未经授权的功能或数据&#xff0c;例如&#xff1a;访问其他用户的帐户、查看敏感文件、修改其…

高速信号应如何走线?这些规则千万别走错

提起高速信号的设计与布线&#xff0c;可能很多工程师都能头头是道&#xff0c;但放在实际设计中却大部分不可行&#xff0c;这篇文章将向电子工程师介绍一些关键规则&#xff0c;确保在电路板上传输高速信号时获得最佳性能&#xff0c;注意这些规则千万别走错&#xff01; 1、…

Java后端开发面试题——JVM虚拟机篇

目录 什么是程序计数器&#xff1f; 你能给我详细的介绍Java堆吗? 什么是虚拟机栈 1. 垃圾回收是否涉及栈内存&#xff1f; 2. 栈内存分配越大越好吗&#xff1f; 3. 方法内的局部变量是否线程安全&#xff1f; 4.什么情况下会导致栈内存溢出&#xff1f; 5.堆栈的区别…

Weblogic SSRF【漏洞复现】

文章目录 漏洞测试注入HTTP头&#xff0c;利用Redis反弹shell redis不能启动问题解决 Path : vulhub/weblogic/ssrf 编译及启动测试环境 docker compose up -dWeblogic中存在一个SSRF漏洞&#xff0c;利用该漏洞可以发送任意HTTP请求&#xff0c;进而攻击内网中redis、fastcgi…

Unity设置TextMeshPro文本超出范围显示...

TextMtshPro文本超出范围&#xff0c;展示省略。选择Overflow为Ellipsis。