最近有word转html功能的需求,收费第三方的不用,网上免费的poi 教程有的阉割了图片处理,有的版本太老,真是一步一个坑。最后都解决了,记录一下,贴出来解决办法,分享一下自己的工具类代码,示例,我所用到的jar包和版本。

如果有想直接word转html,并把图片转成base64一块存到网页中的可以直接看下这篇文章,我是把图片存到本地的:

使用POI实现word转html

另外说一下,word中的图片是在这个地方的
把word的后缀改为zip,并打开

点进去里面的media,里面就是word中的所有图片

说一下遇到的坑和介绍下解决方法

  1. word转html第三方服务收费。(解决办法:使用poi)
  2. poi转出的html内容有问题,比如序号1.变成%1。(解决办法:升级poi版本到4.1.2,低版本解析有问题,抱着试试看的想法升了下,解决了…)
  3. word转html图片处理(docx直接获取)
  4. doc格式图片不好获取。(解决办法:先存到本地(也可以直接存到想存的地方))
  5. (个人需求)doc格式返回到编辑器css格式全部丢失。(解决办法:使用Jsoup)
  6. 本地没问题,放到tomcat服务器提示各种类找不到,doc格式中文乱码。(解决办法:加jar包,乱码在代码中把格式utf-8改为gb2312)在文章结尾我分享下所有的jar包

简单说下第5个坑
docx解析出来的html是这样,css是内联样式

而doc解析出来的html却是这样,css是内部样式,返回编辑器的html head被省去了,所以全部css样式都丢失了。


下文在JsoupUtils贴解决办法…

1 自己的处理步骤

1.1 总体是这样 (代码有删减,去掉了业务代码)

先获取html部分,然后把图片存到自己想存的地方,用Jsoup替换成自己存的图片路径。

public String docImport(MultipartFile file) throws Exception {//返回的html字符串String html = "";//word类型是否docboolean wordType = false;//临时图片存放文件夹 图片会保存在此路径(临时保存)(doc类型使用)String docsTempImages = System.getProperty("java.io.tmpdir") + IdUtil.simpleUUID() + "/";//判断类型if (file.getOriginalFilename().endsWith(".docx") || file.getOriginalFilename().endsWith(".DOCX")) {html = WordToHtmlUtil.Word2007ToHtml(file);} else if (file.getOriginalFilename().endsWith(".doc") || file.getOriginalFilename().endsWith(".DOC")) {wordType = true;html = WordToHtmlUtil.Word2003ToHtmlAndSaveImage(docsTempImages, file);}//获取图片名称和本地url,这一步上传图片到本地,并把名字和url处理成mapString uploadPath = "";//word中的图片上传到哪Map<String, String> imageMaps = WordToHtmlUtil.getImageMaps(uploadPath, docsTempImages, file);//如果有图片替换成本地地址if (!imageMaps.isEmpty()) {html = this.replaceImgToLocal(html, imageMaps, wordType);}return html;}

1.2 代码中的replaceImgToLocal方法

用Jsoup替换掉图片的链接。
doc格式也可以用Jsoup把内部样式转为内联样式

    /*** 替换html图片的路径** @param html* @param imageMaps* @throws IOException*/private String replaceImgToLocal(String html, Map<String, String> imageMaps ,boolean wordType) {String returnHtml = "";//获取当前服务器ip和端口用于图片路径Document doc = Jsoup.parse(html);// 获取 带有src属性的img元素Elements imgTags = doc.select("img[src]");//替换图片for (org.jsoup.nodes.Element element : imgTags) {String imageName = StrUtil.subAfter(element.attr("src"), "/", true);//根据名字获取map中的url,并覆盖之前存的word中的路径element.attr("src", imageMaps.get(imageName));}returnHtml = doc.toString();//doc格式样式在外部,所以要把style从外部移到内部if (wordType) {returnHtml = JsoupUtils.changeHtmlCssLineStyle(doc.toString());}return returnHtml;}

1.3 工具类

工具类是根据我的需求写出来的,可以根据自己的要求改一下。

JsoupUtils

import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;import java.util.HashMap;
import java.util.Map;/*** 将html内部样式转为内联样式** @auther: 胡辣汤麻辣烫* @date: 2021/5/26*/
public class JsoupUtils {private static Map<String, String> getHtmlCss(String html) {org.jsoup.nodes.Document doc = Jsoup.parse(html);String[] styles = doc.head().select("style").html().split("\r\n");Map<String, String> css = new HashMap<>();for (String style : styles) {String[] kv = style.split("\\{|\\}");css.put(kv[0], kv[1]);}return css;}public static String changeHtmlCssLineStyle(String html) {Map<String, String> css = getHtmlCss(html);org.jsoup.nodes.Document doc = Jsoup.parse(html);Element body = doc.body();for (String key : css.keySet()) {body.select(key).attr("style", css.get(key)).outerHtml();}return body.html();}
}

1.4 WordToHtmlUtil


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.URLUtil;
import fr.opensagres.poi.xwpf.converter.xhtml.XHTMLConverter;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.converter.PicturesManager;
import org.apache.poi.hwpf.converter.WordToHtmlConverter;
import org.apache.poi.hwpf.usermodel.PictureType;
import org.apache.poi.xwpf.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;
import org.w3c.dom.Document;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.io.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @auther: 胡辣汤麻辣烫* @date: 2021/5/26*/
public class WordToHtmlUtil {/*** logger*/private static final Logger logger = LoggerFactory.getLogger(WordToHtmlUtil.class);/*** 解析docx成html** @param file* @return* @throws IOException*/public static String Word2007ToHtml(MultipartFile file) throws IOException {if (file.isEmpty() || file.getSize() <= 0) {logger.error("Sorry File does not Exists!");return null;} else {if (file.getOriginalFilename().endsWith(".docx") || file.getOriginalFilename().endsWith(".DOCX")) {// 1) 加载word文档生成 XWPFDocument对象InputStream in = file.getInputStream();XWPFDocument document = new XWPFDocument(in);// 也可以使用字符数组流获取解析的内容ByteArrayOutputStream baos = new ByteArrayOutputStream();XHTMLConverter.getInstance().convert(document, baos, null);String content = baos.toString();baos.close();return content;} else {logger.error("Enter only MS Office 2007+ files");return null;}}}/*** 解析doc文章成html 不存图片** @param file* @return* @throws IOException* @throws ParserConfigurationException* @throws TransformerException*/public static String Word2003ToHtml(MultipartFile file)throws IOException, ParserConfigurationException, TransformerException {if (file.isEmpty() || file.getSize() <= 0) {logger.error("Sorry File does not Exists!");return null;} else {if (file.getOriginalFilename().endsWith(".doc") || file.getOriginalFilename().endsWith(".DOC")) {InputStream input = file.getInputStream();HWPFDocument wordDocument = new HWPFDocument(input);WordToHtmlConverter wordToHtmlConverter = new WordToHtmlConverter(DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument());// 解析word文档wordToHtmlConverter.processDocument(wordDocument);Document htmlDocument = wordToHtmlConverter.getDocument();// 也可以使用字符数组流获取解析的内容ByteArrayOutputStream baos = new ByteArrayOutputStream();DOMSource domSource = new DOMSource(htmlDocument);StreamResult streamResult = new StreamResult(baos);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 = new String(baos.toByteArray());baos.close();return content;} else {logger.error("Enter only MS Office 2003 files");return null;}}}/*** 解析doc成html 并保存图片文件到本地** @param file* @return* @throws IOException* @throws ParserConfigurationException* @throws TransformerException*/public static String Word2003ToHtmlAndSaveImage(String docsTempImages, MultipartFile file)throws IOException, ParserConfigurationException, TransformerException {if (file.isEmpty() || file.getSize() <= 0) {logger.error("Sorry File does not Exists!");return null;} else {if (file.getOriginalFilename().endsWith(".doc") || file.getOriginalFilename().endsWith(".DOC")) {HWPFDocument wordDocument = new HWPFDocument(file.getInputStream());WordToHtmlConverter wordToHtmlConverter = new WordToHtmlConverter(DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument());//设置图片存放的位置wordToHtmlConverter.setPicturesManager(new PicturesManager() {public String savePicture(byte[] content, PictureType pictureType, String suggestedName, float widthInches, float heightInches) {File imgPath = new File(docsTempImages);if (!imgPath.exists()) {//图片目录不存在则创建imgPath.mkdirs();}File file = new File(docsTempImages + suggestedName);try {OutputStream os = new FileOutputStream(file);os.write(content);os.close();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return docsTempImages + suggestedName;}});//解析word文档wordToHtmlConverter.processDocument(wordDocument);Document document = wordToHtmlConverter.getDocument();// 也可以使用字符数组流获取解析的内容ByteArrayOutputStream baos = new ByteArrayOutputStream();DOMSource domSource = new DOMSource(document);StreamResult streamResult = new StreamResult(baos);TransformerFactory factory = TransformerFactory.newInstance();Transformer serializer = factory.newTransformer();
//                serializer.setOutputProperty(OutputKeys.ENCODING, "utf-8");serializer.setOutputProperty(OutputKeys.ENCODING, "gb2312");serializer.setOutputProperty(OutputKeys.INDENT, "yes");serializer.setOutputProperty(OutputKeys.METHOD, "html");serializer.transform(domSource, streamResult);baos.close();// 也可以使用字符数组流获取解析的内容return new String(baos.toByteArray());} else {logger.error("Enter only MS Office 2003 files");return null;}}}/*** 获取word中的图片名称和本地url(doc或docx)* 返回map<图片名称, 存储的图片url地址>** @param uploadPath     图片存放路径* @param docsTempImages 本地临时图片存放地址(这个工具类Word2003ToHtmlAndSaveImage的方法存到了系统临时文件夹里)* @param file* @return* @throws IOException*/public static Map<String, String> getImageMaps(String uploadPath, String docsTempImages, MultipartFile file) throws IOException {//返回mapHashMap<String, String> map = new HashMap<>();if (file.getOriginalFilename().endsWith(".docx") || file.getOriginalFilename().endsWith(".DOCX")) {//获取存在word里的图片文件InputStream in = file.getInputStream();XWPFDocument document = new XWPFDocument(in);List<XWPFParagraph> paragraphs = document.getParagraphs();if (CollUtil.isNotEmpty(paragraphs)) {paragraphs.forEach(p -> {List<XWPFRun> runs = p.getRuns();if (CollUtil.isNotEmpty(runs)) {runs.forEach(r -> {List<XWPFPicture> pictures = r.getEmbeddedPictures();if (CollUtil.isNotEmpty(pictures)) {pictures.forEach(c -> {//这里找到word中的图片的名字和数据XWPFPictureData pictureData = c.getPictureData();String fileName = pictureData.getFileName();byte[] data = pictureData.getData();//保存到本地获取urlString localUrl = saveImageToLocalWithByte(fileName, data, uploadPath);map.put(pictureData.getFileName(), localUrl);});}});}});}} else if (file.getOriginalFilename().endsWith(".doc") || file.getOriginalFilename().endsWith(".DOC")) {try {File dir = new File(docsTempImages);//如果目录不为空遍历存储到项目中if (!FileUtil.isEmpty(dir)) {Arrays.asList(FileUtil.ls(docsTempImages)).forEach(f -> {String name = f.getName();BufferedInputStream inputStream = FileUtil.getInputStream(f);String localUrl = saveImageToLocalWithStream(name, inputStream, uploadPath);map.put(name, localUrl);});}} finally {//删除临时文件夹FileUtil.del(docsTempImages);}}return map;}/*** 保存图片到项目中,返回路径(byte[])** @param name       图片名字* @param data       图片字节数组* @param uploadPath 存储路径* @return*/private static String saveImageToLocalWithByte(String name, byte[] data, String uploadPath) {FileUtil.writeBytes(data, uploadPath + name);//自己项目的ip和端口,html图片地址要用,或者根据自己需求指定存到什么地方,自定义String ipAndPort = "";return URLUtil.normalize(ipAndPort + name);}/*** 保存图片到项目中,返回路径(inputStream)** @param name        图片名字* @param inputStream 输入流* @param uploadPath  存储路径* @return*/private static String saveImageToLocalWithStream(String name, InputStream inputStream, String uploadPath) {savePic(uploadPath, inputStream, name);//自己项目的ip和端口,html图片地址要用,或者根据自己需求指定存到什么地方,自定义String ipAndPort = "";return URLUtil.normalize(ipAndPort + name);}/*** 保存图片** @param path        存储路径* @param inputStream 输入流* @param fileName    文件名称*/private static void savePic(String path, InputStream inputStream, String fileName) {OutputStream os = null;try {// 2、保存到临时文件// 1K的数据缓冲byte[] bs = new byte[1024];// 读取到的数据长度int len;// 输出的文件流保存到本地文件File tempFile = new File(path);if (!tempFile.exists()) {tempFile.mkdirs();}os = new FileOutputStream(tempFile.getPath() + File.separator + fileName);// 开始读取while ((len = inputStream.read(bs)) != -1) {os.write(bs, 0, len);}} catch (IOException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();} finally {// 完毕,关闭所有链接try {os.close();inputStream.close();} catch (IOException e) {e.printStackTrace();}}}
}

2 用到的依赖的maven地址

<!-- 针对2007以上版本的库 --><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.1.2</version></dependency><!-- 针对2003版本的库 --><dependency><groupId>org.apache.poi</groupId><artifactId>poi-scratchpad</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><!-- jsoup --><dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.11.3</version></dependency><!-- hutool--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.0.2</version></dependency>

3 最后分享下部署到tomcat,提示各种类找不到,我最后找到的所有的jar包。

网盘:

链接:https://pan.baidu.com/s/16BHRiUn_Wshv7pJBfu9egg
提取码:gdd9

使用poi解析word转html,并处理word中图片相关推荐

  1. word用宏修改文档中图片大小

    转载自https://blog.csdn.net/pcj2007/article/details/78744429 记录一下,批量改图是一个很好的思路.早就听说了word宏,一直没有用过.今天(201 ...

  2. Java POI解析Word提取数据存储在Excel

    JavaPOI解析word提取数据到excel 一.了解POI POI以前有了解,这次需求是解析word读取其中标题,还有内容赛选获取自己想要的内容 经过两天的学习,开始熟悉Java这么读取word和 ...

  3. 如何使用poi解析word生成html目录结构

    POI解析word目录结构 简介说明 认识下Word 我们先看下doc版本的word 我们再看下docx版本的word(今天的主角) 目录解析的原理介绍 写word文档时,我们是怎么设置目录? 我们看 ...

  4. JAVA Apache POI解析docx格式的word文件并提取带样式文本

    关于JAVA Apache POI读取word文档,网上资料很多,但是大多数还是仅仅提取文档中的纯文本,好一点的,也就提取所有图片,但是,word文档本身是具有样式的,这样简单粗暴的提取就会丢失字体. ...

  5. POI解析03版07版Word

    问题?最近遇到了要解析word的问题,查阅了很多资料才搞定.03版和07版的word POI解析:去官网下载jar,添加如下包即可: 代码: package com.itcast.test;impor ...

  6. poi解析word中的表格

    解析word简历,使用poi解析word表格研究记录如下: package poi;import java.io.File; import java.io.FileInputStream; impor ...

  7. WORD解析:使用poi解析doc和docx

    1.pom依赖 <!-- https://mvnrepository.com/artifact/org.apache.poi/poi --><dependency><gr ...

  8. poi解析word文档(解析表格,emf,wmf,svg转jpg图片)

    POI解析word文档 poi解析word的表格:   提前先准备需要的jar包:   <!-- poi --><dependency><groupId>org.a ...

  9. 【Apache POI】Java Web根据模板导出word文件

    最近工作中遇到一个需求:根据word模板文档导出word文件. 查阅了一些资料,发现Apache POI可以实现文档读写的功能,于是就研究了一下,总结如下: Apache-POI在线Javadoc:h ...

  10. 【操作word】Java + POI导出富文本的内容到word文档

    这周工作中,遇到一个需求是需要将数据库中富文本内容导出到word文档里面,于是就采用POI技术实现了一下导出word文档的功能.(word文档是识别html内容的,所以富文本内容也自然能够识别.) 一 ...

最新文章

  1. Android TextView中文字通过SpannableString来设置超链接、颜色、字体等属性
  2. 几个标准的说明,关于EMC的
  3. 微软正式发布XAML Standard与.NET Standard 2.0:现已提供下载
  4. AUTOSAR专业知识篇(七)-比亚迪汉ECU接口
  5. 初探12306售票算法(一)- 理论
  6. dpkg: 错误: 另外一个进程已经为 dpkg frontend lock 加锁
  7. Libgdx之Table 表格排版
  8. 敌兵布阵(HDU - 1166)(线段树的点更新-区间查询)
  9. 【花雕体验】19 合宙ESP32_C3点亮WS2812B硬屏
  10. Linux 系统不能正常关机解决办法
  11. openstack 单节点 ubuntu 12.04 server 安装步骤 转自陈沙克老师博客
  12. iphone视频照片恢复
  13. 论文写作——origin画图
  14. 梦幻显示器,国产屏幕OLED崛起
  15. iOS 上传头像 裁剪
  16. p1904 p1903
  17. 华为机试【连续出牌数量】
  18. 一本学习C#语言的学习手册(提供下载)
  19. 禁用微软杀毒和移除以Paint 3D打开的相关注册表:
  20. 如何将MP4视频转换为MP3音频

热门文章

  1. 在线教育如何应对流量洪峰?阿里云专家给出了“上云+云数据库”的答案!
  2. 服务器被挖矿入侵,进程 command为ld-linux-x86-64占用cpu很高
  3. 国内外优秀的计算机视觉团队汇总|最新版
  4. 码力十足学量化|如何获取指数成分股及权重数据
  5. python 类继承
  6. IT之软件公司组织架构
  7. mac 微信不能设置代理服务器,mac微信能用但是浏览器上不了网
  8. 【Python】第5次练习:def 定义函数——编写函数求和、质数判断、lambda函数计算三次幂
  9. 七周成为数据分析师(秦路)-第一周-数据分析思维
  10. js中英文字符与中文字符长度区别