poi生成的ppt,powerPoint打开提示内容错误解决方案

news2024/12/27 16:01:29

poi生成的ppt,powerPoint打开提示内容错误解决方案

最近做了ppt的生成,使用poi制作ppt,出现一个问题。微软的powerPoint打不开,提示错误信息

在这里插入图片描述

通过xml对比工具发现只需要删除幻灯片的某些标签即可解决。

用的是XML Notepand
在这里插入图片描述

分析思路:

1.把poi生成的pptx用wps打开,正常,但是poi生成的pptx在powerPoint打不开。尝试用wps另存一份,ok,wps另存后的ppt,powerPoint可以打开了。然后ppt的大小也有了变化,肯定是wps对这个ppt做了一些格式化操作。具体是啥呢,不清楚,只能用这个 XML Notepand 对比看看吧。
2. ppt本质上是xml的压缩包集合。我们对ppt进行解压。

在这里插入图片描述

3.里面的东西挺多了,我自己也不怎么懂,但是我想既然是ppt的某个幻灯片打不开,那我就对比一下能幻灯片,看看poi生成的ppt和wps另存后的ppt的幻灯片有哪些区别。

我们进入这个目录
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

4.这个就是放置幻灯片的位置。

我们通过这个XML Notepand 进行一个对比观察,
在这里插入图片描述
我们先用xml notePand打开poi生成的一个幻灯片,我打开的是wsp另存的解压后的第4个幻灯片,ppt\slides\slide4.xml
在这里插入图片描述

在这里插入图片描述

然后选择这个,再次选择一个poi生成的ppt,选择同一个幻灯片,进行一个对比
在这里插入图片描述
然后进行两个文件对比
在这里插入图片描述

最后对比发现这个标签可能不可以被powerPoint正确识别,我们就把这个标签删除

在这里插入图片描述

<p:custDataLst>
 	<p:tags r:id="rId1"/>
</p:custDataLst>

最关键的来了,直接上代码

package com.ruoyi.ppt.utilsService;

import com.ruoyi.ppt.aopCustom.customAnnotations.LogExecutionTime;
import com.ruoyi.ppt.config.ThreadPoolDealTask;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.w3c.dom.*;

import javax.annotation.Resource;
import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

@Slf4j
@Component
public class PptXmlFormate {

    @Resource
    private ThreadPoolDealTask threadPoolDealTask;
    @LogExecutionTime("PPT XML标签调整耗时")
    public void xmlFormate(String pptxFilePath, String useFont) {
        useFont = StringUtils.isBlank(useFont) ? "宋体" : useFont;

        // 使用 try-with-resources 自动关闭临时文件和 Zip 流
        try {
            // 创建临时文件来存储更新后的 PPTX 文件
            File tempFile = File.createTempFile("pptx_temp" + UUID.randomUUID(), ".zip");
            tempFile.deleteOnExit();

            // 读取 PPTX 文件并更新 XML
            try (FileInputStream fis = new FileInputStream(pptxFilePath);
                 ZipInputStream zis = new ZipInputStream(fis);
                 FileOutputStream fos = new FileOutputStream(tempFile);
                 ZipOutputStream zos = new ZipOutputStream(fos)) {

                ZipEntry entry;
                Map<String, ByteArrayOutputStream> updatedFiles = new HashMap<>();

                // 遍历 PPTX 文件中的每个条目
                while ((entry = zis.getNextEntry()) != null) {
                    String entryName = entry.getName();
                    ByteArrayOutputStream entryData = new ByteArrayOutputStream();

                    // 读取条目内容
                    byte[] buffer = new byte[1024];
                    int len;
                    while ((len = zis.read(buffer)) > 0) {
                        entryData.write(buffer, 0, len);
                    }

                    // 仅处理幻灯片 XML 文件
                    if (entryName.startsWith("ppt/slides/slide") && entryName.endsWith(".xml")) {
                        // 解析 XML
                        try (ByteArrayInputStream bais = new ByteArrayInputStream(entryData.toByteArray());
                             ByteArrayOutputStream updatedXml = new ByteArrayOutputStream()) {

                            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                            factory.setNamespaceAware(true);

                            DocumentBuilder builder = factory.newDocumentBuilder();
                            Document document = builder.parse(bais);


                            // 异步任务,并确保异常捕获
                            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                                // 删除标签
                                String custDataLstXpath = "//*[local-name()='custDataLst']";
                                removeNodesUsingXPath(document, custDataLstXpath);
                                
                            }, threadPoolDealTask.getThreadPoolExecutor());
                            // 捕获并处理异步任务中的异常
                            future.exceptionally(ex -> {
                                log.error("处理 XML 异步任务时发生错误: {}", ex.getMessage(), ex);
                                return null;
                            }).join(); // 等待任务完成

                            // 写入修改后的 XML 内容
                            TransformerFactory transformerFactory = TransformerFactory.newInstance();
                            Transformer transformer = transformerFactory.newTransformer();
                            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
                            DOMSource source = new DOMSource(document);
                            StreamResult result = new StreamResult(updatedXml);
                            transformer.transform(source, result);
                            updatedFiles.put(entryName, updatedXml);

                        }
                    } else {
                        // 对于其他条目,保持原样
                        updatedFiles.put(entryName, entryData);
                    }
                }

                // 写入更新后的 PPTX 文件
                for (Map.Entry<String, ByteArrayOutputStream> fileEntry : updatedFiles.entrySet()) {
                    String entryName = fileEntry.getKey();
                    ByteArrayOutputStream fileData = fileEntry.getValue();

                    zos.putNextEntry(new ZipEntry(entryName));
                    fileData.writeTo(zos);
                    zos.closeEntry();
                }
                zos.finish();
            }

            // 将临时文件移动到原始 PPTX 文件路径,替换原文件
            Files.move(tempFile.toPath(), new File(pptxFilePath).toPath(), StandardCopyOption.REPLACE_EXISTING);

        } catch (IOException e) {
            log.error("处理文件时发生 IO 错误: {}", e.getMessage(), e);
        } catch (Exception e) {
            log.error("处理过程中发生错误: {}", e.getMessage(), e);
        }
    }
    


    public void removeNodesUsingXPath(Document document, String xpathExpression) {
        XPath xPath = XPathFactory.newInstance().newXPath();
        // 设置命名空间前缀和 URI
        NamespaceContext nsContext = new NamespaceContext() {
            public String getNamespaceURI(String prefix) {
                switch (prefix) {
                    case "a":
                        return "http://schemas.openxmlformats.org/drawingml/2006/main";
                    case "p":
                        return "http://schemas.openxmlformats.org/presentationml/2006/main";
                    default:
                        return XMLConstants.NULL_NS_URI;
                }
            }

            public String getPrefix(String namespaceURI) {
                return null;
            }

            public Iterator getPrefixes(String namespaceURI) {
                return null;
            }
        };
        xPath.setNamespaceContext(nsContext);
        try {
            NodeList nodes = (NodeList) xPath.evaluate(xpathExpression, document, XPathConstants.NODESET);

            for (int i = nodes.getLength() - 1; i >= 0; i--) { // 从后向前遍历
                Node node = nodes.item(i);
                Node parentNode = node.getParentNode();
                if (parentNode != null) {
                    parentNode.removeChild(node);
                }
            }
        } catch (Exception e) {
            log.error("Error removing nodes using XPath: {}", e.getMessage());
        }

    }

}

注意哈,这里使用了线程池,不需要的可以删除,需要的话不会配置的,这里也给出代码

package com.ruoyi.ppt.config;


import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;

import javax.annotation.Resource;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;

@Slf4j
@Configuration
@Data
public class ThreadPoolDealTask {



    private volatile ThreadPoolExecutor threadPool;


    // 自定义 ThreadFactory,为线程池中的线程指定名称
    private ThreadFactory createThreadFactory(String poolName) {
        AtomicInteger counter = new AtomicInteger(0);
        return r -> {
            Thread thread = new Thread(r);
            thread.setName(poolName + "-thread-" + counter.incrementAndGet());
            return thread;
        };
    }

    // 懒加载线程池,只有在第一次使用时才会创建 用于 业务处理
    public void initializeThreadPool() {
        if (this.threadPool == null) {
            synchronized (this) {
                if (this.threadPool == null) { // 双重检查锁定
                    this.threadPool = new ThreadPoolExecutor(
                          2,
                            10,
                            30L,
                            TimeUnit.SECONDS,
                            new LinkedBlockingQueue<>(50),
                            createThreadFactory("CustomThreadPool"), // 使用自定义的 ThreadFactory
                            new ThreadPoolExecutor.CallerRunsPolicy()
                    );
                    // 启用核心线程超时
                    this.threadPool.allowCoreThreadTimeOut(false);
                }
            }
        }
    }


    public ThreadPoolExecutor getThreadPoolExecutor() {
        return this.threadPool;

    }

    // 输出任务队列状态
    private void printQueueStatus() {
        if (threadPool instanceof ThreadPoolExecutor) {
            ThreadPoolExecutor executor = (ThreadPoolExecutor) threadPool;
            System.out.println("Submitting task to thread: " + Thread.currentThread().getName());
            System.out.println("当前任务队列大小: " + executor.getQueue().size());
            System.out.println("当前任务队列大小: " + executor.getQueue().size());
            System.out.println("当前活动线程数: " + executor.getActiveCount());
            System.out.println("当前线程池大小: " + executor.getPoolSize());

        }
    }

    // 提交任务并返回Future对象
    public <T> Future<T> submitTask(Callable<T> task) {
        initializeThreadPool(); // 确保线程池已初始化
        return threadPool.submit(task);
    }

    // 提交Runnable任务并返回Future
    public Future<?> submitTask(Runnable task) {
        initializeThreadPool(); // 确保线程池已初始化
        return threadPool.submit(task);
    }

    // 优雅地关闭线程池
    public void shutdown() {
        if (threadPool != null) {
            threadPool.shutdown();
            try {
                // 等待现有任务执行完毕
                if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) {
                    threadPool.shutdownNow(); // 强制关闭
                    // 等待任务响应中断
                    if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) {
                        System.err.println("线程池未能在指定时间内关闭");
                    }
                }
            } catch (InterruptedException ie) {
                // 在当前线程被中断时,也强制关闭线程池
                threadPool.shutdownNow();
                // 保留中断状态
                Thread.currentThread().interrupt();
            } finally {
                threadPool = null; // 关闭后将线程池引用置为空
            }
        }
    }

}

现在解决了xml的标签问题,

最后只需要生成后的ppt地址,传入这个方法,执行一次标签删除即可

处理前效果:
在这里插入图片描述

处理后效果:
在这里插入图片描述

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

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

相关文章

ai写论文哪个平台好?7款AI写论文软件帮你撰写论文

在当今学术研究和写作领域&#xff0c;AI论文写作工具的出现极大地提高了写作效率和质量。这些工具不仅能够帮助研究人员快速生成论文草稿&#xff0c;还能进行内容优化、查重和排版等操作。以下是七款推荐的AI写论文软件&#xff0c;其中特别推荐千笔-AIPassPaper。 一、千笔…

Java集合(Map篇)

一.Map a.使用Map i.键值&#xff08;key-value&#xff09;映射表的数据结构&#xff0c;能高效通过key快速查找value&#xff08;元素&#xff09;。 ii.Map是一个接口&#xff0c;最常用的实现类是HashMap。 iii.重复放入k-v不会有问题&#xff0c;但是一个…

盈科巴黎办公室开业典礼暨盈科全球一小时法律服务生态圈·法国中心揭牌仪式圆满举办

国际化建设是盈科律师事务所发展的重要战略目标之一&#xff0c;随着中国对外投资、跨境贸易、“一带一路”高质量共建等迅速发展&#xff0c;越来越多中国企业和公民“走出去”&#xff0c;寻找海外市场。今年是中法建交60周年。建交以来&#xff0c;中法坚持做真诚的朋友、共…

【Java】JVM基本组成

一、JDK、JRE、JVM JDK&#xff1a;全称 “Java Development Kit” Java 开发工具包&#xff0c;提供 javac编译器、jheap、jconsole 等监控工具; JRE&#xff1a;全称 “Java Runtime Environment” Java 运行环境&#xff0c;提供 class Library 核心类库JVM; …

文件服务器FastDFS 消息队列中间件RabbitMQ

新标签页 (chinaunix.net) FastDFS - Browse Files at SourceForge.net 一、FastDFS Tracker和Storage&#xff1a; tracker用来管理所有的storage&#xff0c;只是管理服务器&#xff0c;负责负载均衡。 storage是存储服务器&#xff0c;每一个storage服务器都是一个单独的个…

计算机毕业设计之:基于微信小程序的疫苗预约系统的设计与实现(源码+文档+讲解)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

Shiro rememberMe反序列化漏洞(Shiro-550)

开启环境抓包验证Shiro框架 使⽤BurpSuite进⾏抓包&#xff0c;在请求包中的cookie字段中添加 rememberMe123; &#xff0c;看响应包 header中是否返回 rememberMedeleteMe 值&#xff0c;若有&#xff0c;则证明该系统使⽤了Shiro框架&#xff1a; 使用工具爆破密钥爆破利用…

论文大杀器!分享4款ai论文写作工具软件

在当今学术研究和论文写作领域&#xff0c;AI技术的应用已经变得越来越普遍。这些工具不仅能够提高写作效率&#xff0c;还能帮助研究人员生成高质量的论文内容。本文将重点介绍四款优秀的AI论文写作工具&#xff0c;并特别推荐千笔-AIPassPaper。 一、千笔-AIPassPaper 传送门…

2024寻找那些能精准修改PDF内容的工具

如今&#xff0c;我们使用 PDF 文档的频率不断攀升&#xff0c;很多时候收到的表格等资料都是 PDF 格式。若先进行格式转换后编辑&#xff0c;再转换回 PDF 格式&#xff0c;着实有些麻烦。那么&#xff0c;pdf怎么编辑修改内容呢&#xff1f;在这篇文章中&#xff0c;我将为大…

python爬虫案例——抓取链家租房信息

文章目录 1、任务目标2、分析网页3、编写代码1、任务目标 目标站点:链家租房版块(https://bj.lianjia.com/zufang/) 要求:抓取该链接下前5页所有的租房信息,包括:标题、详情信息、详情链接、价格 如: 2、分析网页 用浏览器打开链接,按F12或右键检查,进入开发者模式;因…

防火墙--NAT技术,基于源NAT,NAT服务器,双向NAT

文章目录 防火墙--NAT技术一、基于源NAT**方式**&#xff1a;NAT No-PATNAPT出接口地址方式Smart NAT三元组 NAT 二、基于服务器的NAT多出口场景下的NAT Server 三、双向NAT 防火墙–NAT技术 基于源NAT&#xff1a;用于将内部网络的私有IP地址转换为公共IP地址&#xff0c;以便…

使用 IntelliJ IDEA 连接到达梦数据库(DM)

前言 达梦数据库是一款国产的关系型数据库管理系统&#xff0c;因其高性能和稳定性而被广泛应用于政府、金融等多个领域。本文将详细介绍如何在 IntelliJ IDEA 中配置并连接到达梦数据库。 准备工作 获取达梦JDBC驱动&#xff1a; 访问达梦在线服务平台网站或通过其他官方渠道…

Matlab|基于遗传模拟退火算法的风电功率聚类分析

目录 主要内容 部分代码 结果一览 下载链接 主要内容 模糊C-均值聚类&#xff0c;也称FCM&#xff0c;是比较常用的一种聚类算法&#xff0c;该算法利用几何贴进度的概念将不同数据分配到不同聚类群中&#xff0c;但是作为局部搜索优化算法&#xff0c;初值选择不…

Conda虚拟环境配置常见问题记录

搞深度学习的&#xff0c;总有被虚拟环境搞得头大的时候&#xff0c;特别是涉及到CUDA&#xff0c;Torch &#xff0c;Torchvision 版本适配的问题。这两天因为在原来的环境中装了几个包&#xff0c;导致原来的环境崩了&#xff0c;搞了一天的时间又重新配了环境&#xff0c;中…

塑料瓶回收标志分级检测系统源码分享

塑料瓶回收标志分级检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Co…

Etcd权限认证管理

1 查看是否开启权限认证 ctl auth status 2 开启权限认证 ctl auth enable。开启后每一条命令都要加上用户 --userroot:root(root默认最高权限) 3 创建其他用户 ctl user add user1 --user用户名:密码 4 创建角色 ctl role add testR --user 5 为角色添加权限 ctl role g…

python和pyqt-tools安装位置

一.python的安装位置 1.查询安装的python的位置 先查询python&#xff0c;然后输入import sys和sys.path 二.python-tools的安装位置 找到python的文件后按下图路径即可查到tools的文件

OpenAI o1团队突破性论文:『过程推理』中数学推理能力大幅提升,从正确中学习的新方法

原创 超 近年来&#xff0c;大型语言模型(LLMs)在复杂的多步推理任务中取得了令人瞩目的进展。这些模型能够生成逐步的思维链&#xff0c;解决从小学数学到高等微积分的各种问题。然而&#xff0c;即使是最先进的模型也常常陷入逻辑陷阱&#xff0c;产生看似合理但实际错误的推…

PHP校园外卖跑腿小程序带后台(商业版)

有需要请加文章底部Q哦 可远程调试 PHP校园外卖跑腿小程序带后台(商业版) 一 介绍 此校园外卖跑腿小程序端基于原生开发&#xff0c;后端基于ThinkPHP5框架开发&#xff0c;数据库mysql&#xff0c;系统角色分为用户&#xff0c;商家(自配送)&#xff0c;跑腿员&#xff0c;管…

Python+requests+pytest+allure自动化测试框架

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 1、核心库 requests request请求 openpyxl excel文件操作 loggin 日志 smtplib 发送邮件 configparser unittest.mock mock服务 2、目录结构 base utils …