doc与docx文档转html,格式样式不变(包含图片转换)

news2024/11/16 23:42:49

        最近做一个富文本的需求,要求把文档内容转换到富文本内,文档中的格式也好,样式也好,图片啥的都要一致展示;踩了不少坑,据说word文档其实是一个压缩包,我不是特别清楚但是也能理解,自己借鉴参考凑合看的,大佬勿喷

        啥都不说了看代码吧;其中关于图片的导出有两种方式比较大的那种是用的jdk8自带的base

64搞的,大小有差别同一个图片的话我这个实测的图片是差200k左右,有要求的你可以换着来引用;jar的引用pom中有

        <!--注意版本保持一致 poi poi-ooxml  poi-scratchpad-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.1.2</version>
        </dependency>
        <!-- 操作doc ppt xls  -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-scratchpad</artifactId>
            <version>4.1.2</version>
        </dependency>
        <!-- 操作docx pptx xlsx  -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.1.2</version>
        </dependency>
       <dependency>
            <groupId>fr.opensagres.xdocreport</groupId>
            <artifactId>fr.opensagres.poi.xwpf.converter.xhtml</artifactId>
            <version>2.0.2</version>
        </dependency>

import fr.opensagres.poi.xwpf.converter.core.BasicURIResolver;
import fr.opensagres.poi.xwpf.converter.core.FileImageExtractor;
import fr.opensagres.poi.xwpf.converter.xhtml.XHTMLConverter;
import fr.opensagres.poi.xwpf.converter.xhtml.XHTMLOptions;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.converter.WordToHtmlConverter;
import org.apache.poi.hwpf.usermodel.PictureType;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.springframework.web.multipart.MultipartFile;
import org.w3c.dom.Document;
import sun.misc.BASE64Encoder;

import javax.imageio.ImageIO;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Base64;

/**
 * @author :Xiaoning Fan
 * @date :Created in 2023-10-16 下午 3:49
 * @description: 上传word文档并转换为html字符串返回,保持样式不变,图片替换为base64
 * @version: 1.0
 */
@Slf4j
public class WordToHtmlStringConverter {

    /**
     * wordToHtml
     *
     * @return
     * @throws IOException
     * @throws ParserConfigurationException
     * @throws TransformerException
     */
    public static String wordToHtml(MultipartFile file) {
//        提取出word文档名称和后缀
        String filename = file.getOriginalFilename();
        try {
            if (filename.endsWith(".docx")) {
                // 将上传的文件传入Document转换
                return new WordToHtmlStringConverter().docxToHtmlText(file);
            } else if (filename.endsWith(".doc")) {
                return new WordToHtmlStringConverter().docToHtmlText(file);
            } else {
                log.error("不支持的文件格式!");
                return null;
            }
        } catch (FileNotFoundException e) {
            log.error("文件找不到异常!");
            e.printStackTrace();
        } catch (IOException e) {
            log.error("io转换异常!");
            e.printStackTrace();
        } catch (Exception e) {
            log.error("文件转换异常!");
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 上传Word文档,返回解析后的Html
     */
    public static String docToHtmlText(MultipartFile file) throws Exception {
        //使用字符数组流获取解析的内容
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        OutputStream outStream = new BufferedOutputStream(baos);
        try {
            //将上传的文件传入Document转换
            HWPFDocument wordDocument = new HWPFDocument(file.getInputStream());
            Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
            WordToHtmlConverter wordToHtmlConverter = new WordToHtmlConverter(document);
            //将读取到的图片上传并添加链接地址
            wordToHtmlConverter.setPicturesManager((imageStream, pictureType, name, width, height) -> {
                try {
                    //首先要判断图片是否能识别
                    if (pictureType.equals(PictureType.UNKNOWN)) {
                        return "[不能识别的图片]";
                    }
                    //此处转换图片文件为Base64
                    return Base64.getEncoder().encodeToString(imageStream).trim();

                } catch (Exception e) {
                    log.info("upload exception", e);
                }
                return "[图片上传失败]";
            });
            // word文档转Html文档
            wordToHtmlConverter.processDocument(wordDocument);
            Document htmlDocument = wordToHtmlConverter.getDocument();
            DOMSource domSource = new DOMSource(htmlDocument);
            StreamResult streamResult = new StreamResult(outStream);
            TransformerFactory factory = TransformerFactory.newInstance();
            Transformer serializer = factory.newTransformer();
            serializer.setOutputProperty(OutputKeys.ENCODING, "utf-8");
            serializer.setOutputProperty(OutputKeys.INDENT, "yes");
            serializer.setOutputProperty(OutputKeys.METHOD, "html");
            serializer.transform(domSource, streamResult);
            String content = baos.toString();
            log.info("docToHtmlText--->{}", content);
            return content;
        } catch (Exception e) {
            log.error("docToHtmlText 异常", e);
        } finally {
            baos.close();
            outStream.close();
        }
        return null;
    }

    /**
     * 上传docx文档,返回解析后的Html
     */
    public static String docxToHtmlText(MultipartFile file) throws Exception {
        ByteArrayOutputStream htmlStream = new ByteArrayOutputStream();
        ByteArrayOutputStream htmlImg = new ByteArrayOutputStream();
        String htmlStr = null;
        try {
            // 将上传的文件传入Document转换
            XWPFDocument docxDocument = new XWPFDocument(file.getInputStream());
            XHTMLOptions options = XHTMLOptions.create();
            // 设置图片存储路径
            String path = System.getProperty("java.io.tmpdir");
            String firstImagePathStr = path + "/" + System.currentTimeMillis();
            options.setExtractor(new FileImageExtractor(new File(firstImagePathStr)));
            options.URIResolver(new BasicURIResolver(firstImagePathStr));
            // 转换html
            docxDocument.createNumbering();
            XHTMLConverter.getInstance().convert(docxDocument, htmlStream, options);
            htmlStr = htmlStream.toString();

            String middleImageDirStr = "/word/media";
            String imageDirStr = firstImagePathStr + middleImageDirStr;
            File imageDir = new File(imageDirStr);
            String[] imageList = imageDir.list();
            if (imageList != null) {
                for (int i = 0; i < imageList.length; i++) {
                    try {
                        String oneImagePathStr = imageDirStr + "/" + imageList[i];
                        File fileImage = new File(oneImagePathStr);
                        if (fileImage.exists()) {
                            log.info("处理图片开始。。。。。。。。");
                            // 处理图片成为Base64格式
                            // 读取图片字节数组
                            InputStream in = new FileInputStream(fileImage);
                            byte[] data = new byte[in.available()];
                            in.read(data);
                            String encode = new BASE64Encoder().encode(data);
                            log.info("处理图片结束。。。。。。。" + encode);
                            //修改文档中的图片信息
                            htmlStr = htmlStr.replace(oneImagePathStr, "data:image/png;base64,"+encode);

                           /* BufferedImage bi = ImageIO.read(fileImage);// 图片存储大小比较大
                            ByteArrayOutputStream baos = new ByteArrayOutputStream();
                            ImageIO.write(bi, "png", baos);
                            byte[] bytes = baos.toByteArray();
                            String sd =  Base64.getEncoder().encodeToString(bytes).trim();
                            log.info("处理图片结束。。。。。。。" + sd);
                            htmlStr = htmlStr.replace(oneImagePathStr, "data:image/png;base64,"+sd);*/


                        }
                    } catch (Exception e) {
                        log.info("upload docxToHtmlText exception", e);
                    }
                }
            }
            log.info("处理结果:{}", htmlStr);
        } catch (Exception e) {
            log.error("docxToHtmlText 解析异常", e);
        } finally {
            if (htmlStream != null) {
                htmlStream.close();
            }
            return htmlStr;
        }
    }
}

直接引用就行,但是有一点,一定要注意接口返回的时候,如果直接返回页面接口上要加

@ResponseBody不然就悲剧了;当然如果直接存库的那就无所谓了

这次就先这样,自娱自乐,手下留情勿喷!!

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

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

相关文章

爬虫用什么库更事半功倍?

1、首先&#xff0c;我们需要安装 TypeScript 和 superagent 库。在命令行中运行以下命令来安装它们&#xff1a; npm install typescript npm install superagent2、创建一个新的 TypeScript 项目&#xff0c;并在项目中创建一个名为 crawler 的文件夹。在 crawler 文件夹中&a…

Qt判断一个点在多边形内还是外(支持凸边形和凹变形)

这里实现的方法是转载于https://blog.csdn.net/trj14/article/details/43190653和https://blog.csdn.net/WilliamSun0122/article/details/77994526 来实现的&#xff0c;并且按照Qt的规则进行了调整。 以下实现方法有四种&#xff0c;每种方法的具体讲解在转载的博客中有说明&…

低代码到底是什么?

究竟什么样的新技术&#xff0c;才能真正解放IT生产力&#xff0c;加速社会数字化转型&#xff0c;Make The World Great Again&#xff1f;我认为是低代码&#xff08;Low-Code&#xff09;。 “Low-Code”是什么&#xff1f;“Code”是指代码&#xff0c;但这个“Low”字是啥…

DFS(分布式文件系统)与 DFSR(分布式文件系统复制)的区别

DFS&#xff08;分布式文件系统&#xff09;和 DFSR&#xff08;分布式文件系统复制&#xff09;是两种不同的技术&#xff0c;尽管它们在名称上有一些相似之处&#xff0c;但它们的用途和功能有所不同。 DFS&#xff08;分布式文件系统&#xff09; DFS 是一种用于创建和管理…

tinker官网加载demo的使用流程

tinker官网加载demo的使用流程 0&#xff0c;首先开接入指南&#xff1a; https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97 1&#xff0c;在gradle 找到tinker的插件&#xff0c;来判断tinker是否集成成功。 2&#xff0c;安装一个现在有…

TikTok:年轻一代的创新驱动力与社会影响

在当今数字媒体和社交网络的时代&#xff0c;TikTok已经崭露头角&#xff0c;成为一个风靡全球的短视频平台&#xff0c;尤其受到年轻一代的热烈欢迎。 但TikTok不仅仅是一个娱乐应用&#xff0c;它也代表着年轻一代的创新驱动力和社会影响力的集大成者。本文将深入探讨TikTok…

【微信小程序】6天精准入门(第4天:自定义组件及案例界面)附源码

一、自定义组件 1、介绍 从小程序基础库版本 1.6.3 开始&#xff0c;小程序支持简洁的组件化编程。所有自定义组件相关特性都需要基础库版本 1.6.3 或更高。 开发者可以将页面内的功能模块抽象成自定义组件&#xff0c;以便在不同的页面中重复使用&#xff1b;也可以将复杂的页…

LiveQing视频点播流媒体RTMP推流服务功能-支持视频点播分屏大屏展示视频轮巡分组播放RMP推流直播大屏展示

LiveQing支持视频点播分屏大屏展示视频轮播分组播放RMP推流直播大屏展示 1、分屏展示2、轮巡播放3、RTMP推流视频直播和点播流媒体服务 1、分屏展示 LiveQing支持将视频点播、鉴权直播&#xff0c;拉转直播视频流&#xff0c;进行分屏播放。 2、轮巡播放 3、RTMP推流视频直播和…

CRC16计算FC(博途SCL语言)

CRC8的计算FC,相关链接请查看下面文章链接: 博途SCL CRC8 计算FC(计算法)_博途怎么计算crc_RXXW_Dor的博客-CSDN博客关于CRC8的计算网上有很多资料和C代码,这里不在叙述,这里主要记录西门子的博途SCL完成CRC8的计算过程, CRC校验算法,说白了,就是把需要校验的数据与多项式…

企业数字化转型时,会遇到的5大挑战

企业数字化转型时&#xff0c;会遇到的5大挑战添加链接描述 数字化转型已然是当今商业战略的一大基石&#xff0c;根据Gartner的《2023年度董事会调查》显示&#xff0c;有89%的企业将数字业务视为其增长的核心。但该研究的另一项统计数据也显示&#xff1a;在这些企业中&…

会议OA小程序【会议管理,个人中心页面布局】

目录 一. 自定义组件介绍 1.1 概念 1.2 创建自定义组件 二. 会议管理页面布局 使用自定义组件 页面布局及样式 三. 个人中心页面布局 一. 自定义组件介绍 1.1 概念 从小程序基础库版本 1.6.3 开始&#xff0c;小程序支持简洁的组件化编程。所有自定义组件相关特性都需…

新服务入驻生产环境 CICD 全流程、自动化脚本教程

文章目录 背景CICD百花齐放 “四部曲”实现优势涉及文件核心流程ci.ymlMakefilepackage.shnoah_control 小结 背景 新服务功能完成测试后&#xff0c;将会进行生产环境的入住&#xff0c;对外提供产品、功能支持。那么如何规范的、安全的、自动化的把本地服务移植到生产环境呢…

uniapp无感刷新token实现过程

路漫漫其修远兮&#xff0c;前端道路逐渐迷茫&#xff0c;时隔好久好久终于想起了我还有一个小博客&#xff0c;最近在一直在弄uniapp&#xff0c;属实有被恶心到&#xff0c;但也至少会用了&#xff0c;最近实现了一个比较通用的功能&#xff0c;就是无感刷新token&#xff0c…

如何下载和安装 Linux Red Hat 9.0安装包

【微|信|公|众|号&#xff1a;厦门微思网络】 官网&#xff1a; www.xmws.cn 【限时优惠】RHCE9.0培训考证-红帽官方授权中心-CSDN博客通过这门课程&#xff0c;您将能够更好的理解企业级需求和解决方案&#xff0c;提升您的战略思 维和决策能力并助力您为企业升级使用新的技…

【C++】引用之带你“消除”C语言版数据结构教材的一些困惑(虽然是C++的内容,但是强烈建议正在学习数据结构的同学点进来看看)

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 引用的概念 引用的特性 引用的使用场…

Django实现音乐网站 ⒇

使用Python Django框架做一个音乐网站&#xff0c; 本篇音乐播放器-添加播放音乐功能实现。 目录 创建播放器数据表 设置表结构 执行创建表 命令 执行 数据表结构 添加单个歌曲 创建路由 加入播放器视图 模板处理 基类方法 子页面调用 优化弹窗 加入layui文件 基…

DPDK收发包流程分析

一、 前言 DPDK是intel工程师开发的一款用来快速处理数据包的框架,最初的目的是为了证明传统网络数据包处理性能低不是intel处理器导致的,而是传统数据的处理流程导致,后来随着dpdk的开源及其生态的快速发展,dpdk成为了高性能网络数据处理的优秀框架。本篇文章主要介绍DPDK…

游戏动态库缺失

缺哪个动态库就搜哪个&#xff0c;再下载下来。 百度网盘&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1TlxLtL3hg_iCCvtCzT7bXw 提取码&#xff1a;8888 文件下载完之后要放到指定的位置 C:\Windows\System32

怎么在爬虫中使用ip代理服务器,爬虫代理IP的好处有哪些?

随着互联网的快速发展&#xff0c;网络爬虫已经成为数据采集、分析和整理的重要工具。然而&#xff0c;随着网络技术的不断发展&#xff0c;许多网站都会采取反爬虫措施&#xff0c;以避免数据被恶意获取。在这种情况下&#xff0c;代理IP服务器就成为了爬虫们的必本备文工将具…

Flink学习---15、FlinkCDC(CDC介绍、案例实操)

星光下的赶路人star的个人主页 未来总是藏在迷雾中让人胆怯&#xff0c;但当你踏入其中&#xff0c;便会云开雾散 文章目录 1、CDC简介1.1 什么是CDC1.2 CDC的种类1.3 Flink-CDC 2、FlinkCDC案例实操2.1 开启MySQL Binlog并重启MySQL2.2 FlinkSQL方式的应用2.2.1 导入依赖2.2.2…