在业务中,如果需要在浏览器中预览Word文档,或者需要将Word文档转成HTML文件保存,那么本章内容,可以帮助到你。
实现这一功能,有多种实现方式,如:docx4j、poi、Free Spire.Doc for Java、openoffice、jacob都可以实现转换功能,但都有局限性。在这稍微介绍一下哈,大家可做个对比
docx4j
docx4j主要是针对docx文件进行操作,操作的对象的Microsoft Open XML文件。
java当中用于操作office(docx/xlsx/ppt)等文件的类库
poi
POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能。
结构:
HSSF - 提供读写Microsoft Excel格式档案的功能。
XSSF - 提供读写Microsoft Excel OOXML格式档案的功能。
HWPF - 提供读写Microsoft Word格式档案的功能。
HSLF - 提供读写Microsoft PowerPoint格式档案的功能。
HDGF - 提供读写Microsoft Visio格式档案的功能。
Free Spire.Doc for Java(功能强大,但可以收费)
Free Spire.Doc for Java 是一款免费、专业的 Java Word 组件,开发人员使用它可以轻松地将 Word 文档创建、读取、编辑、转换和打印等功能集成到自己的 Java 应用程序中。作为一款完全独立的组件,Free Spire.Doc for Java的运行环境无需安装 Microsoft Office。
友情提示:免费版有篇幅限制。在加载或保存 Word 文档时,要求 Word 文档不超过 500 个段落,25 个表格。同时将 Word 文档转换为 PDF 和 XPS 等格式时,仅支持转换前三页
openoffice
一、利用jodconverter(基于OpenOffice服务)将文件(.doc、.docx、.xls、.ppt)转化为html格式。
二、利用jodconverter(基于OpenOffice服务)将文件(.doc、.docx、.xls、.ppt)转化为pdf格式。需要用户安装了Adobe Reader XI
jacob(不能用于Linux)
需要引入jacob.jar jar包,并且jar包还要调用jacob.dll文件,需要事先把jacob.dll文件放到以下3处地方:C:\Windows\System32 目录下,安装的jdk文件夹下的bin目录中,以及jre文件夹下的bin目录(注意一定是你这个项目运行所用到的jdk和jre)
它允许在java中调用com接口自动组件,它使用JNI(本地调用进程)来进行本地调用COM库。它可运行在x86和支持32位和64位Java虚拟机
本文采用poi来进行转换
<!-- WordToHtml .doc .odcx poi --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-scratchpad</artifactId> <version>4.1.2</version> </dependency> <!-- 操作excel的库 注意版本保持一致 poi poi-ooxml poi-scratchpad --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.1.2</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml --> <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> <!-- https://mvnrepository.com/artifact/fr.opensagres.xdocreport/fr.opensagres.xdocreport.converter.docx.xwpf --> <dependency> <groupId>fr.opensagres.xdocreport</groupId> <artifactId>fr.opensagres.xdocreport.converter.docx.xwpf</artifactId> <version>2.0.1</version> </dependency>
/** * poi WordToHtml工具类 */ @Slf4j public class WordToHtml { // 文件上传保存路径 @Value(value = "${upload.path}") private final String uploadPath = ""; //转换的方法 public File convert(MultipartFile file) { //获得文件的名字 String filename = file.getOriginalFilename(); //获得文件的扩展名 String suffix = filename.substring(filename.lastIndexOf(".")); String newName = UUID.randomUUID().toString(); // TODO 需要保存在一个新的位置 // File =new File 表示目录的一个抽象,可以进一步用exists()和isDirectory()方法判断。 File convFile = new File(uploadPath + newName + suffix); FileOutputStream fos = null; try { //创建文件 convFile.createNewFile(); //FileOutputStream 是输出流 将文件输出到磁盘或者数据库中 fos = new FileOutputStream(convFile); fos.write(file.getBytes()); } catch (IOException ex) { log.error("上传文件出错!", ex); return null; } finally { IOUtils.closeQuietly(fos); } // 输入文件名的所在文件夹 // 加上反斜杠 String parentDirectory = convFile.getParent(); if (!parentDirectory.endsWith("\\")) { parentDirectory = parentDirectory + "\\"; } if (filename.endsWith(".docx")) { return docxConvert(parentDirectory, convFile.getAbsolutePath(), newName); } else if (filename.endsWith(".doc")) { return docConvert(parentDirectory, convFile.getAbsolutePath(), newName); } else { log.error("不支持的文件格式!"); return null; } } /** * html 流文件 修改内容 width:595.3pt; 因为转换的HTML页面默认内容区域不是html自适应大小,内容位置不对 * @param parentDirectory html文件所在文件夹 * @param filename html旧文件地址 * @param newName html新文件地址 * @return */ private File htmlreplace(String parentDirectory, String filename, String newName) { try { // 读取生成的Html FileInputStream inputStream = new FileInputStream(new File(parentDirectory + filename + ".html")); InputStream inputStrem = readInputStrem(inputStream); // 清空文件内容 clearInfoForFile(parentDirectory + filename + ".html"); // TODO: 2022/4/22 进行流输出Html文件 inputStrem // 1、读取内容 byte[] buffer = new byte[inputStrem.available()]; inputStrem.read(buffer); // 写入内容 OutputStream outStream = new FileOutputStream(new File(parentDirectory + newName + ".html")); outStream.write(buffer); return new File(parentDirectory + newName + ".html"); } catch (FileNotFoundException e) { log.error("Html转换失败!",e); return null; } catch (IOException e) { log.error("Html转换失败!",e); return null; } } /** * 读取HTML 流文件,并查询当中的width:595.3pt; / white-space:pre-wrap; 或类似符号直接替换为空格 * * @param inputStream * @return */ private static InputStream readInputStrem(InputStream inputStream) { // 匹配内容 String regEx_special = "width:595.3pt;"; String regEx_special2 = "white-space:pre-wrap;"; // 替换新内容 String replace = "white-space:pre-wrap;word-break:break-all;"; try { //<1>创建字节数组输出流,用来输出读取到的内容 ByteArrayOutputStream baos = new ByteArrayOutputStream(); //<2>创建缓存大小 byte[] buffer = new byte[1024]; // 1KB //每次读取到内容的长度 int len = -1; //<3>开始读取输入流中的内容 while ((len = inputStream.read(buffer)) != -1) { //当等于-1说明没有数据可以读取了 baos.write(buffer, 0, len); //把读取到的内容写到输出流中 } //<4> 把字节数组转换为字符串 String content = baos.toString(); //<5>关闭输入流和输出流 // inputStream.close(); baos.close(); // log.info("读取的内容:{}", content); // 判断HTML内容是否具有HTML的 width:595.3pt; Pattern compile = Pattern.compile(regEx_special, Pattern.CASE_INSENSITIVE); Matcher matcher = compile.matcher(content); String replaceAll = matcher.replaceAll(""); // 判断是否具有white-space:pre-wrap; Pattern compile2 = Pattern.compile(regEx_special2, Pattern.CASE_INSENSITIVE); Matcher matcher2 = compile2.matcher(replaceAll); String replaceAll2 = matcher2.replaceAll(replace); // log.info("替换后的内容:{}", replaceAll2); // 将字符串转化为输入流返回 InputStream stringStream = getStringStream(replaceAll2); //<6>返回结果 return stringStream; } catch (Exception e) { e.printStackTrace(); log.error("错误信息:{}", e.getMessage()); return null; } } /** * 将一个字符串转化为输入流 * @param sInputString 字符串 * @return */ public static InputStream getStringStream(String sInputString) { if (sInputString != null && !sInputString.trim().equals("")) { try { ByteArrayInputStream tInputStringStream = new ByteArrayInputStream(sInputString.getBytes()); return tInputStringStream; } catch (Exception e) { e.printStackTrace(); } } return null; } /** * 清空文件内容 * @param fileName */ public static void clearInfoForFile(String fileName) { File file =new File(fileName); try { if(!file.exists()) { file.createNewFile(); } FileWriter fileWriter =new FileWriter(file); fileWriter.write(""); fileWriter.flush(); fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } } /** * 转换.docx 当word文档字体大于5号字体时,会出现不规律排列文字换行(因为转换的HTML页面默认内容区域不是html原始区域) * @param parentDirectory html文件所在文件夹 (主要用于图像的管理) * @param filename word文件地址 * @param newName html文件地址 * @return */ private File docxConvert(String parentDirectory, String filename, String newName) { try { // 1) 加载word文档生成 XWPFDocument对象 XWPFDocument document = new XWPFDocument(new FileInputStream(filename)); // 设置存放图片地址 XHTMLOptions options = XHTMLOptions.create().setImageManager(new ImageManager(new File(parentDirectory), UUID.randomUUID().toString())).indent(4); OutputStream out = new FileOutputStream(new File(parentDirectory + newName + ".html")); // 自定义编码格式 OutputStreamWriter writer = new OutputStreamWriter(out,"GBK"); // 生成HTML XHTMLConverter xhtmlConverter = (XHTMLConverter)XHTMLConverter.getInstance(); xhtmlConverter.convert(document, writer, options); // 将生成的HTML进行内容匹配替换 File htmlreplace = htmlreplace(parentDirectory, newName, newName); return htmlreplace; // return new File(parentDirectory + newName + ".html"); } catch (IOException ex) { log.error("word转化出错!", ex); return null; } } /** * 转换.doc * @param parentDirectory html文件所在文件夹 (主要用于图像的管理) * @param filename word文件地址 * @param newName html文件地址 * @return */ private File docConvert(String parentDirectory, String filename, String newName) { try { HWPFDocument document = new HWPFDocument(new FileInputStream(filename)); WordToHtmlConverter wordToHtmlConverter = new WordToHtmlConverter( DocumentBuilderFactory.newInstance().newDocumentBuilder() .newDocument()); // converter默认对图片不作处理,需要手动下载图片并嵌入到html中 wordToHtmlConverter.setPicturesManager(new PicturesManager() { @Override public String savePicture(byte[] bytes, PictureType pictureType, String s, float v, float v1) { String imageFilename = parentDirectory + ""; String identity = UUID.randomUUID().toString(); File imageFile = new File(imageFilename, identity + s); imageFile.getParentFile().mkdirs(); InputStream in = null; FileOutputStream out = null; try { in = new ByteArrayInputStream(bytes); out = new FileOutputStream(imageFile); IOUtils.copy(in, out); } catch (IOException ex) { log.error("word转化出错!", ex); } finally { if (in != null) { IOUtils.closeQuietly(in); } if (out != null) { IOUtils.closeQuietly(out); } } return imageFile.getName(); } }); wordToHtmlConverter.processDocument(document); Document htmlDocument = wordToHtmlConverter.getDocument(); ByteArrayOutputStream out = new ByteArrayOutputStream(); DOMSource domSource = new DOMSource(htmlDocument); StreamResult streamResult = new StreamResult(out); // 设置转换属性 TransformerFactory tf = TransformerFactory.newInstance(); Transformer serializer = tf.newTransformer(); serializer.setOutputProperty(OutputKeys.ENCODING, "GBK"); serializer.setOutputProperty(OutputKeys.INDENT, "yes"); serializer.setOutputProperty(OutputKeys.METHOD, "html"); serializer.transform(domSource, streamResult); out.close(); String result = new String(out.toByteArray()); FileWriter writer = new FileWriter(parentDirectory + newName + ".html"); writer.write(result); writer.close(); } catch (IOException | TransformerException | ParserConfigurationException ex) { log.error("word转化出错!", ex); } return new File(parentDirectory + newName + ".html"); } /** * 将上传的Word文档转化成HTML字符串 * * @param file * @return */ public String convertToHtml(MultipartFile file) { String wordContent = ""; // 将Word文件转换为html File file2 = convert(file); // 读取html文件 if (file2 != null) { return "文件转换成功"; } return "文件转换失败"; } /** * wordToHtml * @param wordFilePath word文件路径 * @param htmlFilePath html文件路径 * @throws IOException * @throws ParserConfigurationException * @throws TransformerException */ public static File wordToHtml(String wordFilePath,String htmlFilePath) { // 提取出word文档名称和后缀 String filename = wordFilePath.substring(wordFilePath.lastIndexOf("/")+1); // 提取出html文件存放路径和文件名称 String newName = htmlFilePath.substring(htmlFilePath.lastIndexOf("/")+1,htmlFilePath.lastIndexOf(".")); File convFile = new File(htmlFilePath); // 输入文件名的所在文件夹 // 加上反斜杠 String parentDirectory = convFile.getParent(); if (!parentDirectory.endsWith("\\")) { parentDirectory = parentDirectory + "\\"; } if (filename.endsWith(".docx")) { return new WordToHtml().docxConvert(parentDirectory, wordFilePath, newName); } else if (filename.endsWith(".doc")) { return new WordToHtml().docConvert(parentDirectory, wordFilePath, newName); } else { log.error("不支持的文件格式!"); return null; } } }
/** * @Author:wk * @Create:2022/4/21/15:10 * @Description:WordToHtml测试类 poi * @Version:1.0 */ @Slf4j public class WordToHtmlTest { public static void main(String[] args) { long timeMillis = System.currentTimeMillis(); log.info("开始转换!"); String wordFilePath = "src/main/resources/word/nc.docx"; String htmlFilePath = "src/main/resources/html/nc5.html"; File file = WordToHtml.wordToHtml(wordFilePath, htmlFilePath); // 读取html文件 if (file != null) { log.info("文件存放路径:{}",file.getPath()); log.info("转换结束!用时:{}ms",System.currentTimeMillis()-timeMillis); return; } log.error("文件转换失败!"); } }
测试效果(真实效果存在较小差异)由于截图一页显示不全,所以文档和页面都做了响应调整哈
.doc
.docx(文档是一样的, 此处就不截屏了哈)