关于HTML转word小记

HTML转word

背景介绍

业务:将平台中笔记(富文本)以word形式导出。

1. 使用POI进行转化

依赖jar
poi-3.17.jar
poi-excelant-3.17.jar
poi-ooxml-3.17.jar
poi-ooxml-schemas-3.17.jar
jsoup-1.11.3.jar

1.1 思路

a). 获取HTML
b). HTML标准化
c). 转化

1.2 代码示例

/**
  * HTML转word
  *
  * @param noteName 导出文件名称
  * @paramre portDirName 文件路径
  * @param researchNoteInfo 文件的html
  * @return void
  * @author Solitary
  * @date 2019/1/11 9:21
  */
public static void htmlToWord(String noteName, String reportDirName, String researchNoteInfo) throws Exception {
     //拼一个标准的HTML格式文档
    Document document = Jsoup.parse(researchNoteInfo);
    FileUtils.fileIsExist(reportDirName);
    InputStream is = new ByteArrayInputStream(document.html().getBytes("GBK"));
    OutputStream os = new FileOutputStream(reportDirName+noteName);
    inputStreamToWord(is, os);
}
    
/**
 * 把is写入到对应的word输出流os中
 *
 * @param is
 * @param os
 * @throws IOException
 */
private static void inputStreamToWord(InputStream is, OutputStream os) throws IOException {
    POIFSFileSystem fs = new POIFSFileSystem();
    DirectoryNode root = fs.getRoot();
    root.createDocument("WordDocument", is);
    fs.writeFilesystem(os);
    os.close();
    is.close();
}

1.3 思考

使用poi进行HTML转word的确很简单的,但是一个很棘手的问题就是当图片资源失效、断网的情况下,word的图片无法显示。所以使用这种方式转word显得有点鸡肋了。

2. 使用jacob进行转化

依赖jar
jacob.jar
jsoup-1.11.3.jar

2.1 思路

a). 标准化HTML
b). 下载图片资源到本地
c). 替换所有img标签为<p>${img_imgName}</p>
d). 将替换后的HTML写入空白文档doc中
e). 复制上一步写入文档的内容,替换所有${img_imgName}为本地图片路径
f). 另存为doc文件

2.2 代码示例

/**
 * 创建空白文档_写入html_处理空白文档image_复制空白文档至最终文档
 * 
 * @param imgs
 * @param html
 * @param localpath
 */
 public static String getWord(String html, String localpath, long researchId) {
     // 下载图片到本地 略
     // 图片在文档中的键${name} - 值图片的绝对路径    imgMap.put("${ABC}", localpath + "\\ABC.png");
     Map<String, String> imgMap = new HashMap<String, String>();
     // 解析html_创建空白文档_html写入空白文档
     Document document = Jsoup.parse(html);
     for (Element element : document.body().select("body > *")) {
     	sysElementText(element, localpath, imgMap);
	 }
     createWord(localpath, "blank");
     File doc = new File(localpath + File.separator + "blank.doc");
     FileWriter fw;
     try {
         fw = new FileWriter(doc);
         fw.write(document.html(), 0, document.html().length());// 写入文件
         fw.flush(); // 清空FileWriter缓冲区
         fw.close();
     } catch (IOException e) {
         e.printStackTrace();
     }
     String complete = String.valueOf(researchId);
     // 复制空白文档-粘贴到临时文档(相当于手动执行copy_paste)
     MSOfficeGeneratorUtils officeUtils = new MSOfficeGeneratorUtils(false);
     officeUtils.openDocument(localpath + File.separator + "blank.doc");
     officeUtils.copy(); // 拷贝整篇文档
     officeUtils.close();
     officeUtils.createNewDocument();
     officeUtils.paste(); // 粘贴整篇文档
     // 将图片${image_name}替换为真实图片
     for (Entry<String, String> entry : imgMap.entrySet())
         officeUtils.replaceText2Image(entry.getKey(), entry.getValue());
     
     officeUtils.setFont(true, false, false, "0,0,0", "20", "宋体"); // 设置字体,具体参数
     officeUtils.saveAs(localpath + File.separator + complete + ".doc"); // 可生成UUID.doc文件,利用UUID防止同名
     officeUtils.close(); // 关闭Office Word创建的文档
     officeUtils.quit(); // 退出Office Word程序
     imgMap.clear();
     return complete;
 }
 
/**
 *  替换img标签为p标签
 * 
 * @param node 
 * @param imgPath 本地图片存储路径
 * @param imgMap key:${imgName} value:
 */
public static void sysElementText(Node node, String imgPath, Map<String, String> imgMap) {
  	if (node.childNodes().size() == 0) {
  		if (node.nodeName().equals("img")) {
  			String src = node.attr("src");
			String fileName = src.substring(src.lastIndexOf("/") + 1, src.length());
			Element element = new Element("p");
			element.append("${"+fileName+"}");
			element.attr("style", node.attr("style"));
			node.replaceWith(element);
			imgMap.put("${"+fileName+"}", imgPath + File.separator + fileName);
  		}
  	}
  	if (node.childNodes().size() > 0) {
  		List<Node> childNodes = node.childNodes();
  		for (Node node2 : childNodes) {
  			if (node2.nodeName().equals("img")) {
  				String src = node2.attr("src");
  				String fileName = src.substring(src.lastIndexOf("/") + 1, src.length());
  				Element element = new Element("p");
  				element.append("${"+fileName+"}");
  				element.attr("style", node2.attr("style"));
  				node2.replaceWith(element);
  				imgMap.put("${"+fileName+"}", imgPath + File.separator + fileName);
  			}
		}
  	}
}

    /**
     * 创建word文档
     * 
     * @param localpath
     * @param name
     * @return
     */
    public static void createWord(String localpath, String name) {
        MSOfficeGeneratorUtils msOfficeUtils = new MSOfficeGeneratorUtils(false); // 整合过程设置为可见
        msOfficeUtils.createNewDocument();
        msOfficeUtils.saveAs(localpath + File.separator + name + ".doc");
        msOfficeUtils.close();
        msOfficeUtils.quit();
    }

MSOfficeGeneratorUtils该类参考:http://www.cnblogs.com/liudaihuablogs/p/9761297.html

2.3 思考

该方式转换图片正常显示,唯一不足的地方在于jacob使用的是office的api,服务器必须是windows,在linux下是不能运行的,所以很奔溃。
于是,我们申请一台windows服务器,在该调用HTML转word前,发送消息给windows服务器执行生成word。之后,通过Smb服务,java中的SmbFile获取远程文件到本地。

3. 使用itext进行转化

依赖jar
itext-2.1.7.jar
itext-rtf-2.1.7.jar

3.1 思路

a). 将img标签中的src修改为本地图片路径
b). 以rtf方式导出为word

3.2 代码示例

private static void html2WordIText(String html, String noteName, String reportDirName, long researchId) {
	// 图片临时存放路径
	String pwd = "tmp/researchNote";
	Set<String> srcSet = ImageUtils.getImgStr(html);
	for (String src : srcSet) {
		String srcName = src.split("[/]")[src.split("[/]").length - 1];
		ImageUtils.download(src, srcName, pwd + "/" + String.valueOf(researchId));
		String newSrc = pwd + "/" + String.valueOf(researchId) + "/" + srcName;
		html = html.replace(src, newSrc);
	}
	FileUtils.fileIsExist(reportDirName);
	OutputStream out = null;
	try {
		out = new FileOutputStream(reportDirName + noteName);
		Document document = new Document(PageSize.A4);
		RtfWriter2.getInstance(document, out);
		document.open();
		Paragraph context = new Paragraph();
		// Image img = Image.getInstance("D:\\图片\\2.jpg");
		// img.setAbsolutePosition(0, 0);//
		// document.add(img);
		StyleSheet ss = new StyleSheet();
		HashMap<String, String> interfaceProps = Maps.newHashMap();
		interfaceProps.put("img_baseurl", "");
		List htmlList = HTMLWorker.parseToList(new StringReader(html), ss, interfaceProps);
		for (int i = 0; i < htmlList.size(); i++) {
			com.lowagie.text.Element e = (com.lowagie.text.Element) htmlList.get(i);
			context.add(e);
		}
		document.add(context);
		document.close();
		FileUtils.deletefile(pwd);
	} catch (Exception e) {
		
	} finally {
		try {
			if (out != null) {
				out.close();
			}
		} catch (IOException e) {
			
		}
	}
}

ImageUtils.java :
public static Set<String> getImgStr(String htmlStr) {
       Set<String> pics = new HashSet<String>();
       String img = "";
       Pattern pImage;
       Matcher mImage;
       String regEx_img = "<img.*src\\s*=\\s*(.*?)[^>]*?>";
       pImage = Pattern.compile(regEx_img, Pattern.CASE_INSENSITIVE);
       mImage = pImage.matcher(htmlStr);
       while (mImage.find()) {
           // 得到<img />数据
           img = mImage.group();
           // 匹配<img>中的src数据 
           Matcher m = Pattern.compile("src\\s*=\\s*\"?(.*?)(\"|>|\\s+)").matcher(img);
           while (m.find()) {
               pics.add(m.group(1));
           }
       }
       return pics;
   }

 /**
	 * 下载图片
	 * @param urlString  路径
	 * @param filename   保存的文件名
	 * @param savePath   保存路径
	 */
	public static void download(String urlString, String filename, String savePath) {
	    InputStream is = null;
	    OutputStream os = null;
	    try {
	    	File researchFile = new File(savePath + File.separator + filename);
	    	if (researchFile.exists()) {
	    		return;
	    	}
	    	 // 构造URL
	        URL url = new URL(urlString);
	        // 打开连接
	        URLConnection con = url.openConnection(new Proxy();
	        // 设置请求超时为5s
	        con.setConnectTimeout(5*1000);
	        con.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)"); 
	        // 输入流
	        is = con.getInputStream();
	
	        // 1K的数据缓冲
	        byte[] bs = new byte[1024 * 1024 * 3];
	        // 读取到的数据长度
	        int len;
	        // 输出的文件流
	        File sf=new File(savePath);
	        if(!sf.exists()){
	            sf.mkdirs();
	        }
	        os = new FileOutputStream(sf.getPath() + File.separator + filename);
	        // 开始读取
	        while ((len = is.read(bs)) != -1) {
	            os.write(bs, 0, len);
	        }
	        // 完毕,关闭所有链接
	        os.close();
	        is.close();
	    } catch (IOException e) {
	        e.printStackTrace();
	    } finally {
	    	if (os != null) {
	    		try {
					os.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
	    	}
	    }
	}

4. 总结

方法一断网无法显示图片,方法二linux下无法生成,方法三图片大小不易调整;但总体来说方法三优于前两者。如有错误还请指正,谢谢。

    原文作者:委屈的巴扎黑
    原文地址: https://blog.csdn.net/Solitary_J/article/details/86611202
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞