java在Excel中添加png图片作为页眉(已解决)

news2025/7/12 23:59:43

1.背景

Excel在打印的时候需要附带水印打出,水印需要在下载Excel文件时就设置好
需要在打印的时候打印出水印,使用添加背景添加的水印在打印的时候不会出现,只有设置页眉,并将页眉设置为一长与打印纸张一样大小的图片,才可以在每一页中打印出水印

2.出现的问题

需要在打印的时候打印出水印,使用添加背景添加的水印在打印的时候不会出现,所以只有设置页眉,并将页眉设置为一长与打印纸张一样大小的图片,才可以在每一页中打印出水印,因为poi默认只支持字符串页眉设置,具体原因可以看下面参考文章。最终找到下面两种实现方法

参考:
poi源码详解默认只支持字符串页眉原因

3.实现方法

1 使用poi修改xlsx里面的xml文件来实现我们自己的页眉
2 使用Spire.XLS为Excel添加图片到页眉

有两种实现方法,推荐第一种,因为第一种是基于poi的另外扩展实现图片页眉,只要能拿到XSSFSheet就可以无缝切换到这种方式,能实现平滑迁移。
第二种方式jar包本身比较大,只用一个设置页眉的功能,依赖比较重。而且第二种只能在Excel处理完成后使用本地读取或者使用流读取在重新设置页眉,需要大幅修改原来代码输入输出流方式,整体调整会比较大。

4.使用poi修改xlsx里面的xml文件来实现我们自己的页眉

4.1 原理说明:

Excel本质上来说是一种xml描述文件,可以反向找到页眉设置的位置,然后改动xlsx里面的xml文件添加vmlDrawing1.vml页眉文件实现我们自己的页眉。

4.2 重写POIXMLDocumentPart 中的commit方法,写入我们自定义的vml文件

导入依赖

<!--poi导入导出组件-->
<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>ooxml-schemas</artifactId>
	<version>1.0</version>
</dependency>

```java
import org.apache.poi.POIXMLDocumentPart;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.xmlbeans.XmlObject;

import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
import java.io.IOException;
import java.io.OutputStream;

public class VmlDrawing extends POIXMLDocumentPart {

    String             rIdPic         = "";
    String             pictureTitle   = "";
    java.awt.Dimension imageDimension = null;
    String             headerPos      = "";

    VmlDrawing(PackagePart part) {
        super(part);
    }

    void setRIdPic(String rIdPic) {
        this.rIdPic = rIdPic;
    }

    void setPictureTitle(String pictureTitle) {
        this.pictureTitle = pictureTitle;
    }

    void setHeaderPos(String headerPos) {
        this.headerPos = headerPos;
    }

    void setImageDimension(java.awt.Dimension imageDimension) {
        this.imageDimension = imageDimension;
    }

    @Override
    protected void commit() throws IOException {
        PackagePart part = getPackagePart();
        OutputStream out  = part.getOutputStream();
        try {
            // WPS测试通过版本
            XmlObject doc = XmlObject.Factory.parse(

                    "<xml xmlns:v=\"urn:schemas-microsoft-com:vml\""
                            + " xmlns:o=\"urn:schemas-microsoft-com:office:office\""
                            + " xmlns:x=\"urn:schemas-microsoft-com:office:excel\">"
                            + " <o:shapelayout v:ext=\"edit\">"
                            + "  <o:idmap v:ext=\"edit\" data=\"1\"/>"
                            + " </o:shapelayout><v:shapetype id=\"_x0000_t75\" coordsize=\"21600,21600\" o:spt=\"75\""
                            + "  o:preferrelative=\"t\" path=\"m@4@5l@4@11@9@11@9@5xe\" filled=\"f\" stroked=\"f\">"
                            + "  <v:stroke joinstyle=\"miter\"/>"
                            + "  <v:formulas>"
                            + "   <v:f eqn=\"if lineDrawn pixelLineWidth 0\"/>"
                            + "   <v:f eqn=\"sum @0 1 0\"/>"
                            + "   <v:f eqn=\"sum 0 0 @1\"/>"
                            + "   <v:f eqn=\"prod @2 1 2\"/>"
                            + "   <v:f eqn=\"prod @3 21600 pixelWidth\"/>"
                            + "   <v:f eqn=\"prod @3 21600 pixelHeight\"/>"
                            + "   <v:f eqn=\"sum @0 0 1\"/>"
                            + "   <v:f eqn=\"prod @6 1 2\"/>"
                            + "   <v:f eqn=\"prod @7 21600 pixelWidth\"/>"
                            + "   <v:f eqn=\"sum @8 21600 0\"/>"
                            + "   <v:f eqn=\"prod @7 21600 pixelHeight\"/>"
                            + "   <v:f eqn=\"sum @10 21600 0\"/>"
                            + "  </v:formulas>"
                            + "  <v:path o:extrusionok=\"f\" gradientshapeok=\"t\" o:connecttype=\"rect\"/>"
                            + "  <o:lock v:ext=\"edit\" aspectratio=\"t\"/>"
                            + " </v:shapetype><v:shape id=\"" + headerPos + "\" o:spid=\"_x0000_s1025\" type=\"#_x0000_t75\""
                            + "  style='position:absolute;margin-left:0;margin-top:0;"
                            + "width:" + (int) imageDimension.getWidth() + "px;height:" + (int) imageDimension.getHeight() + "px;"
                            + "z-index:1'>"
                            + "  <v:imagedata o:relid=\"" + rIdPic + "\" o:title=\"" + pictureTitle + "\"/>"
                            + "  <o:lock v:ext=\"edit\" rotation=\"t\"/>"
                            + " </v:shape></xml>"

            );
            doc.save(out, DEFAULT_XML_OPTIONS);
            out.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

}

4.3 使用PackagePartName将图片打包进xml,然后使用重写的VmlDrawing写入vml页眉到对应文件目录位置

import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.openxml4j.opc.PackagePartName;
import org.apache.poi.openxml4j.opc.PackagingURIHelper;
import org.apache.poi.ss.usermodel.Header;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.ImageUtils;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.usermodel.*;

   /**
     * 为Excel设置图片页眉,大体思路为改动xlsx里面的xml文件来实现我们自己的页眉
     * @param sheet
     * @param waterRemarkPath
     * @param position
     * @throws Exception
     */
    public static void putWaterRemarkToExcel(XSSFSheet sheet, String waterRemarkPath, String position)throws Exception {
        InputStream inputStream = WaterMarkHandler.class.getResourceAsStream("/jpg/平台水印.png");
        byte[] byteData = FileUtil.readInputStream(inputStream);
        int pictureIdx = sheet.getWorkbook().addPicture(byteData, Workbook.PICTURE_TYPE_PNG);
        OPCPackage opcpackage = ((XSSFWorkbook)sheet.getWorkbook()).getPackage();
        PackagePartName partname = PackagingURIHelper.createPartName("/xl/drawings/vmlDrawing" + pictureIdx + ".vml");
        PackagePart part = opcpackage.createPart(partname, "application/vnd.openxmlformats-officedocument.vmlDrawing");
        VmlDrawing vmldrawing = new VmlDrawing(part); //创建页眉,位置LEFT,下面headerPos填写对应的
        Header header = sheet.getHeader();
        switch (position) {
            case "LEFT":
                header.setLeft("&G");
                vmldrawing.setHeaderPos("LH");
                break;
            case "CENTER":
                header.setCenter("&G");
                vmldrawing.setHeaderPos("CH");
                break;
            case "RIGHT":
                header.setRight("&G");
                vmldrawing.setHeaderPos("RH");
                break;
            default:
                throw new IllegalArgumentException("输入的position参数不合法");
        }
        XSSFPictureData picData = (XSSFPictureData)sheet.getWorkbook().getAllPictures().get(pictureIdx);
        String rIdPic = vmldrawing.addRelation(null, XSSFRelation.IMAGES, picData).getRelationship().getId();
        ByteArrayInputStream is = new ByteArrayInputStream(picData.getData());
        java.awt.Dimension imageDimension = ImageUtils.getImageDimension(is, picData.getPictureType());
        IOUtils.closeQuietly(is);
        vmldrawing.setRIdPic(rIdPic);
        vmldrawing.setPictureTitle(waterRemarkPath);
        vmldrawing.setImageDimension(imageDimension);
        String rIdExtLink = ((XSSFSheet)sheet).addRelation(null, XSSFRelation.VML_DRAWINGS, vmldrawing).getRelationship().getId();
        ((XSSFSheet)sheet).getCTWorksheet().addNewLegacyDrawingHF().setId(rIdExtLink);
    }

参考:
poi 版本_poi的excel图片水印生成
easypoi代码git地址

5.使用Spire.XLS为Excel添加图片到页眉

5.1 下载jar包后放入工程/resources/lib本地,pom引入

在这里插入图片描述

		<dependency>
			<groupId>e-iceblue</groupId>
			<artifactId>spire.xls.free</artifactId>
			<version>5.1.0</version>
			<scope>system</scope>
			<systemPath>${project.basedir}/src/main/resources/lib/spire.xls.free-5.1.0.jar</systemPath>
		</dependency>
		
		<!--includeSystemScope 修改pom打包,将本地libjar打入-->
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<version>2.1.4.RELEASE</version>
				<configuration>
					<includeSystemScope>true</includeSystemScope>
				</configuration>
				<executions>
					<execution>
						<goals>
							<goal>repackage</goal>
						</goals>
					</execution>
				</executions>
			</plugin>

5.2 设置页眉为图片示例代码

public class ImageHeader {
    public static void main(String[] args) throws IOException {
        String imageFile = "C:\\Users\\Test1\\Desktop\\Image.png";

        //加载Excel示例文档
        Workbook workbook = new Workbook();
        workbook.loadFromFile("C:\\Users\\Test1\\Desktop\\Sample.xlsx");

        //获取第一个工作表
        Worksheet worksheet = workbook.getWorksheets().get(0);

        //加载图片
        BufferedImage image = ImageIO.read( new File(imageFile));

        //设置图片页眉
        worksheet.getPageSetup().setLeftHeaderImage(image);
        worksheet.getPageSetup().setLeftHeader("&G");

        //设置显示样式
        worksheet.setViewMode(ViewMode.Layout);

        //保存文档
        workbook.saveToFile("output/ImageHeader.xlsx", ExcelVersion.Version2010);
    }
}

参考:
使用Spire.XLS为Excel添加图片到页眉

6.扩展,EsayExcel设置图片页眉

6.1 实现SheetWriteHandler的afterSheetCreate方法,调用putWaterRemarkToExcel为页眉添加图片水印

import com.alibaba.excel.write.handler.SheetWriteHandler;
/**
 * excel添加水印,只支持XSSFWorkbook,其余类别:SXSSFWorkbook、SXSSFWorkbook请另寻他法
 * easyExcel使用时需要设置inMemory(true),否者默认使用的是SXSSFWorkbook,会报错!
 */
@Slf4j
@RequiredArgsConstructor
public class WaterMarkHandler implements SheetWriteHandler {
 
    private final String WATER_MARK;

    @Override
    public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
    }
 
    @Override
    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
        // 获取sheet表
        XSSFSheet sheet = (XSSFSheet) writeSheetHolder.getSheet();
        try {
            putWaterRemarkToExcel(sheet, WATER_MARK, "LEFT");
        } catch (Exception e) {
            log.info("添加水印失败, Message : {}", e.getMessage(), e);
        }
    }
}

6.2 导出示例,inMemory一定要配置

@GetMapping("/export")
public void export(HttpServletResponse response) throws IOException {
    response.setContentType("application/vnd.ms-excel");
    response.setCharacterEncoding("utf-8");
    // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
    String fileName = URLEncoder.encode("导出测试", "UTF-8");
    response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
    EasyExcel.write(response.getOutputStream(), DemoData.class)
                .inMemory(true) // 注意,此项配置不能少
                .registerWriteHandler(new WaterMarkHandler("我是水印"))
                .sheet("模板")
                .doWrite(data());
}

参考:
EasyExcel导出添加水印(设置背景,非插入图片的方式)

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

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

相关文章

Objective-C 字符串拼接函数 多个不同类型的参数拼接到一个字符串 类似于Java中 String.format()方法的原生API

总目录 iOS开发笔记目录 从一无所知到入门 文章目录需求ScreenshotCodeOutput需求 我有多个参数(类型也许不同)&#xff0c;需要拼接到一个字符串中。 在Java中有String.format()方法可以做到一次性格式转换。 在Objective-C中呢&#xff1f;也有具有类似功能的API&#xff1a…

从0探索NLP——导航帖

从0探索NLP——导航帖 人工智能是一个定义宽泛、知识组成复杂的领域&#xff0c;而NLP是人工智能领域中的一类任务&#xff0c;他在哪呢&#xff1f;Emmmmm~不能说都有涉猎只能说全都都沾点&#xff1a; 每次想要针对NLP的某一点进行讲解时&#xff0c;不讲那写细枝末节&…

【Git】Git下载安装与使用(一)

目录 1. 前言 1.1 什么是Git 1.2 使用Git能做什么 2. Git概述 2.1 Git简介 2.2 Git下载与安装 3. Git代码托管服务 3.1 常用的Git代码托管服务 3.2 码云代码托管服务 1. 前言 1.1 什么是Git Git是一个分布式版本控制工具&#xff0c;主要用于管理开发过程中的源代码…

多线程知识点

多线程 基本知识 创建线程的常用三种方式&#xff1a; 继承Thread类实现Runnable接口实现Callable接口&#xff08;JDK1.5>&#xff09; public class ThreadTest extends Thread {Overridepublic void run() {System.out.println(this.getName() "..开始.."…

国内动漫绘画培训班盘点

动漫培训机构哪家好&#xff1f;动漫培训班是一种有效的提升动漫水平的方式&#xff0c;可以帮助学生提高绘画技巧和技能&#xff0c;更好地表达自己的艺术想法。 一&#xff1a;动漫培训班排名 1、轻微课&#xff08;五颗星&#xff09; 主打课程有日系插画、游戏原画、古风插…

C语言进阶(二)—— 指针强化

1. 指针是一种数据类型1.1 指针变量指针是一种数据类型&#xff0c;占用内存空间&#xff0c;用来保存内存地址。void test01(){int* p1 0x1234;int*** p2 0x1111;printf("p1 size:%d\n",sizeof(p1));printf("p2 size:%d\n",sizeof(p2));//指针是变量&am…

Python实现贝叶斯优化器(Bayes_opt)优化卷积神经网络分类模型(CNN分类算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。1.项目背景贝叶斯优化器(BayesianOptimization) 是一种黑盒子优化器&#xff0c;用来寻找最优参数。贝叶斯优化器是基…

Redis+Caffeine多级(二级)缓存,让访问速度纵享丝滑

目录多级缓存的引入多级缓存的优势CaffeineRedis实现多级缓存V1.0版本V2.0版本V3.0版本多级缓存的引入 在高性能的服务架构设计中&#xff0c;缓存是一个不可或缺的环节。在实际的项目中&#xff0c;我们通常会将一些热点数据存储到Redis或MemCache这类缓存中间件中&#xff0…

【100个 Unity实用技能】☀️ | Unity 通过自定义菜单将资源一键导出

Unity 小科普 老规矩&#xff0c;先介绍一下 Unity 的科普小知识&#xff1a; Unity是 实时3D互动内容创作和运营平台 。包括游戏开发、美术、建筑、汽车设计、影视在内的所有创作者&#xff0c;借助 Unity 将创意变成现实。Unity 平台提供一整套完善的软件解决方案&#xff…

ESP32设备驱动-内置电容触摸传感器

内置电容触摸传感器 文章目录 内置电容触摸传感器1、电容触摸传感器介绍2、软件准备3、硬件准备4、代码实现本文将详细介绍如何使用ESP32的内置电容式传感器。 1、电容触摸传感器介绍 ESP32 具有可用作触摸按钮的电容式传感器。 这些是引脚排列上著名的TOUCH引脚。 在开发板的…

vs2019+opencv450+opencv contrib450+cmake3.25.2安装流程

目的 为了研究利用sift、orb、surf等算法进行视觉特征检测&#xff0c;第一次配置折腾了四五天。 电脑环境 win10 opencv4.5.0 opencv contrib 4.5.0 cmake3.25.2 报错 问题1 OpenCV(3.4.3) Error: The function/feature is not implemented (This algorithm is patented…

基于QUIC 协议的HTTP/3

HTTP/2 存在一些比较严重的与 TCP 协议相关的缺陷&#xff0c;但由于 TCP 协议僵化&#xff0c;我们几乎不可能通过修改 TCP 协议自身来解决这些问题&#xff0c;那么解决问题的思路是绕过 TCP 协议&#xff0c;发明一个 TCP 和 UDP 之外的新的传输协议。但是这也面临着和修改 …

Dart 表达式以及语法糖汇总

前言 Dart语言中有许多语法糖或者说lambda表达式&#xff0c;语法和代码量是简洁了许多&#xff0c;但给想要入门的我添加了许多困扰&#xff0c;我经常看官方API或者第三方文档API的时候&#xff0c;在示例中大量的使用了类似的语法糖&#xff0c;让代码的可读性大大下降&…

内部知识管理应该怎么做?

许多公司都知道需要有一个面向客户的知识库&#xff0c;以加强客户服务&#xff0c;提供更好的客户体验。 但是很多企业没有意识到的是&#xff0c;拥有一个内部知识库软件对于员工改善沟通和促进知识共享的重要性。 协作是组织成功的关键部分&#xff0c;通过明确的远景和使命…

微服务之Ribbon负载均衡

&#x1f3e0;个人主页&#xff1a;阿杰的博客 &#x1f4aa;个人简介&#xff1a;大家好&#xff0c;我是阿杰&#xff0c;一个正在努力让自己变得更好的男人&#x1f468; 目前状况&#x1f389;&#xff1a;24届毕业生&#xff0c;奋斗在找实习的路上&#x1f31f; &#x1…

论文笔记:DropMessage: Unifying Random Dropping for Graph Neural Networks

&#xff08;AAAI 23 优秀论文&#xff09; 1 intro GNN的一个普遍思路是&#xff0c;每一层卷积层中&#xff0c;从邻居处聚合信息 尽管GNN有显著的进步&#xff0c;但是在大规模图中训练GNN会遇到各种问题&#xff1a; 过拟合 过拟合之后&#xff0c;GNN的泛化能力就被限制…

Matplotlib精品学习笔记001-图形绘制常见的组分有哪些?

简介 从头学习&#xff0c;逐步精美 学习蓝本 学习资料是Quick start 内容 所有绘图的起始步骤 import matplotlib as mpl import matplotlib.pyplot as plt import numpy as np通过一个简单的例子认识Matplotlib绘图的过程&#xff0c;见代码注释 import matplotlib.py…

重温Python基础知识点,又来重新巩固一遍

前言 最近有很多朋友刚接触python学的还是有点模糊 还有的朋友就是想重温一下基础内容&#xff0c;毕竟基础不牢地动山摇 行吧&#xff0c;就总结了以下的一些知识点&#xff0c;可以都看看哈 一、开发环境搭建 更多学习资料.点击领取即可 1.1 Python解释器的安装 Python解…

ctfshow 代码审计专题

文章目录web 301web 302web 303web 304web 305web 306web 307web 308web 309web 310web 301 简单看一下&#xff0c;在checklogin.php中发现了sql语句&#xff0c;且没过滤&#xff0c;直接sql注入。 –form测试,–batch绕过waf.–dump列出所有库和表。 得到账号密码&#xf…

MySQL数据库————MVCC

MySQL的脏读、幻读、不可重复读 脏读 现在有两个事务在操作table表&#xff0c;事务B修改了id2的name字段为李老四&#xff0c;但是没有提交&#xff0c;事务A查询id2的数据&#xff0c;得到name为李老四&#xff1b;事务B发生回滚&#xff0c;id2的数据的name又变回李四&…