前一篇文章介绍了后台将前台html转为word文档的一种方式,但却有一个问题是没法讲图片放置在生成的word报告中。我在网上找了很多方法,甚至将图片转换成base64编码的方式也不成功。效果如下:

  由于本人的水平有限,因此使用其他的实现方式。

   首先介绍一下前台呈现图片的原理:前台ueditor编辑框呈现的图片实际上是一个img变迁,呈现的图片的原始文件是存在服务器上的(甚至在udeitor中直接粘贴图片也是想服务器上传了该图片)。

  

  对应服务器文件为:

  因此,我的视线思路是先将页面传回后台的html内容中的img标签使用特定的字符替换,直接生成一个文本文档,然后在在对应img标签的位置替换为相应的图片。因为在看网上说不能直接操作doc文档插入图片,需要生成docx文档,于是我又参考网上实现了生成docx的后台代码。

  这次生成word文档使用的是docx4j,插入图片需要引入poi的另一个依赖,所需依赖如下:

        <dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.3.1</version>
</dependency> <dependency>
<groupId>org.docx4j</groupId>
<artifactId>docx4j</artifactId>
<version>3.3.6</version>
</dependency>
<dependency>
<groupId>org.docx4j</groupId>
<artifactId>docx4j-ImportXHTML</artifactId>
<version>3.3.6</version>
</dependency>

  代码实现如下:

   @RequestMapping("/defectV2/defect/analysis/tranformHtmlToWord")
@ResponseBody
public MessageBean tranformHtmlToWordDocx(@RequestParam Map params,HttpServletRequest request, HttpServletResponse response) {
try {
// params包含前台传回的html内容
analysisService.tranformHtmlToWordDocx(params,request,response);
return new MessageBean("success", "生成报告成功!", null);
} catch (Exception e) {
e.printStackTrace();
utils.WriteSystemLog(sls, "ERROR", "生成报告", "生成报告失败!" + e.getCause());
return new MessageBean("error", "生成报告失败!", null);
}
}
public String tranformHtmlToWordDocx(Map params, HttpServletRequest request, HttpServletResponse response) throws Exception {
String body = (String) params.get("editorValue"); List<String> imgList = new ArrayList<String>();
String imgTag = "";//img标签
String imgRegex = "<img[^>]+/>";
Pattern imgPattern = Pattern.compile(imgRegex,Pattern.CASE_INSENSITIVE);
Matcher imgMatcher = imgPattern.matcher(body);
int count = 1;
while (imgMatcher.find()){
imgTag = imgMatcher.group(); //获取img标签 String srcStr = "";
//获取匹配img标签中的src内容
Matcher srcMatcher = Pattern.compile("src\\s*=\\s*\"?(.*?)(\"|>|\\s+)").matcher(imgTag);
while (srcMatcher.find()){
srcStr = srcMatcher.group(1);
} String contextPath = request.getContextPath();
String imagePath = ExportWord.getRealPath() + srcStr.substring(srcStr.indexOf(contextPath) + contextPath.length() +1);
imgList.add(imagePath);
body = body.replace(imgTag,"${img" + count + "}");
count ++;
}
//html内容(&nbsp;需要被替换,否则导出会报错)
        String unescaped ="<!DOCTYPE html><html><head><title>导出word</title></head><body>" + body.replaceAll("&nbsp;","    ")
+"</body></html>";
if (unescaped.contains("&lt;/") ) {
unescaped = StringEscapeUtils.unescapeHtml(unescaped);
} // 创建一个空的docx对象
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
XHTMLImporter importer = new XHTMLImporterImpl(wordMLPackage);
importer.setTableFormatting(FormattingOption.IGNORE_CLASS);
importer.setParagraphFormatting(FormattingOption.IGNORE_CLASS); NumberingDefinitionsPart ndp = new NumberingDefinitionsPart();
wordMLPackage.getMainDocumentPart().addTargetPart(ndp);
ndp.unmarshalDefaultNumbering(); // 转换XHTML,并将其添加到我们制作的空docx中
XHTMLImporterImpl XHTMLImporter = new XHTMLImporterImpl(wordMLPackage); XHTMLImporter.setHyperlinkStyle("Hyperlink");
wordMLPackage.getMainDocumentPart().getContent().addAll(
XHTMLImporter.convert(unescaped,null));
    //ExportWord.getWordPath()是获取项目文件路径
String wordPath = ExportWord.getWordPath() + new Date().getTime() + "问题分析统计.docx";
wordMLPackage.save(new File(wordPath)); Map<String, Object> testMap = new HashMap<String, Object>();
WordUtils wordUtil=new WordUtils();
if (imgList!=null && imgList.size()>0){
for (int i=0;i<imgList.size();i++){
String imagePath = imgList.get(i);
Map map = new HashMap();
//封装的单个图片信息
map = WordUtils.packageImgMessage(imagePath);
testMap.put("${img" + (i+1) + "}",map);
}
}
wordUtil.getWord(wordPath,testMap,new ArrayList<String[]>(),"质量分析统计报告.docx",response);
return null;
}

WordUtil.java实现代码(参考了网上的)

public class WordUtils {

    /**
* 根据模板生成word
* @param path 模板的路径
* @param params 需要替换的参数
* @param tableList 需要插入的参数
* @param fileName 生成word文件的文件名
* @param response
*/
public void getWord(String path, Map<String, Object> params, List<String[]> tableList, String fileName, HttpServletResponse response) throws Exception {
File file = new File(path);
InputStream is = new FileInputStream(file);
CustomXWPFDocument doc = new CustomXWPFDocument(is);
this.replaceInPara(doc, params); //替换文本里面的变量
this.replaceInTable(doc, params, tableList); //替换表格里面的变量
OutputStream os = response.getOutputStream();
response.setHeader("Content-disposition", "attachment; filename=" + URLEncoder.encode(fileName,"utf-8"));
doc.write(os);
this.close(os);
this.close(is); } /**
* 替换段落里面的变量
* @param doc 要替换的文档
* @param params 参数
*/
private void replaceInPara(CustomXWPFDocument doc, Map<String, Object> params) {
Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
XWPFParagraph para;
while (iterator.hasNext()) {
para = iterator.next();
this.replaceInPara(para, params, doc);
}
} /**
* 替换段落里面的变量
*(网络上找到的)
* @param para 要替换的段落
* @param params 参数
*/
private void replaceInParaYuan(XWPFParagraph para, Map<String, Object> params, CustomXWPFDocument doc) {
List<XWPFRun> runs;
Matcher matcher;
if (this.matcher(para.getParagraphText()).find()) {
runs = para.getRuns();
int start = -1;
int end = -1;
String str = "";
for (int i = 0; i < runs.size(); i++) {
XWPFRun run = runs.get(i);
String runText = run.toString();
//找出“${”开始的XWPFRun
if ('$' == runText.charAt(0) && '{' == runText.charAt(1)) {
start = i;
}
//若有“${”,则记录开始的XWPFRun
if ((start != -1)) {
str += runText;
}
//若有“}”,记录结束的XWPFRun
if ('}' == runText.charAt(runText.length() - 1)) {
if (start != -1) {
end = i;
break;
}
}
} //${变量}之间的XWPFRun
for (int i = start; i <= end; i++) {
para.removeRun(i);
i--;
end--;
} for (Map.Entry<String, Object> entry : params.entrySet()) {
String key = entry.getKey();
if (str.indexOf(key) != -1) {
Object value = entry.getValue();
if (value instanceof String) {
str = str.replace(key, value.toString());
para.createRun().setText(str, 0);
break;
} else if (value instanceof Map) {
str = str.replace(key, "");
Map pic = (Map) value;
int width = Integer.parseInt(pic.get("width").toString());
int height = Integer.parseInt(pic.get("height").toString());
int picType = getPictureType(pic.get("type").toString());
byte[] byteArray = (byte[]) pic.get("content");
ByteArrayInputStream byteInputStream = new ByteArrayInputStream(byteArray);
try {
//int ind = doc.addPicture(byteInputStream,picType);
//doc.createPicture(ind, width , height,para);
doc.addPictureData(byteInputStream, picType);
doc.createPicture(doc.getAllPictures().size() - 1, width, height, para);
para.createRun().setText(str, 0);
break;
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
} /**
* 替换段落里面的变量
* (优化后的)
* @param para 要替换的段落
* @param params 参数
*/
private void replaceInPara(XWPFParagraph para, Map<String, Object> params, CustomXWPFDocument doc) {
List<XWPFRun> runs;
Matcher matcher;
String paraText = para.getParagraphText();
if(matcher(paraText).find()){
runs = para.getRuns();
for (int i=0;i<runs.size();i++){
XWPFRun run = runs.get(i);
String runText = run.toString();
matcher = matcher(runText);
List<String> $StrList = new ArrayList<String>(); while (matcher.find()){
$StrList.add(matcher.group());
} if ($StrList.size()>0){
// para.removeRun(i);
for (String $Str : $StrList){
if (params.containsKey($Str)){
Object value = params.get($Str);
if (value instanceof String) {
runText = runText.replace($Str, value.toString());
// para.createRun().setText(runText, 0);
// break;
} else if (value instanceof Map) {
runText = runText.replace($Str, "");
Map pic = (Map) value;
int width = Integer.parseInt(pic.get("width").toString());
int height = Integer.parseInt(pic.get("height").toString());
int picType = getPictureType(pic.get("type").toString());
byte[] byteArray = (byte[]) pic.get("content");
ByteArrayInputStream byteInputStream = new ByteArrayInputStream(byteArray);
try {
//int ind = doc.addPicture(byteInputStream,picType);
//doc.createPicture(ind, width , height,para);
doc.addPictureData(byteInputStream, picType);
doc.createPicture(doc.getAllPictures().size() - 1, width, height, para);
// para.createRun().setText(runText, 0);
// break;
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
// para.createRun().setText(runText, 0);
run.setText(runText,0);
} }
}
} /**
* 为表格插入数据,行数不够添加新行
*
* @param table 需要插入数据的表格
* @param tableList 插入数据集合
*/
private static void insertTable(XWPFTable table, List<String[]> tableList) {
//创建行,根据需要插入的数据添加新行,不处理表头
for (int i = 0; i < tableList.size(); i++) {
XWPFTableRow row = table.createRow();
}
//遍历表格插入数据
List<XWPFTableRow> rows = table.getRows();
int length = table.getRows().size();
for (int i = 1; i < length - 1; i++) {
XWPFTableRow newRow = table.getRow(i);
List<XWPFTableCell> cells = newRow.getTableCells();
for (int j = 0; j < cells.size()&&tableList!=null&&tableList.size()>0; j++) {
XWPFTableCell cell = cells.get(j);
String s = tableList.get(i - 1)[j];
cell.setText(s);
}
}
} /**
* 替换表格里面的变量
* @param doc 要替换的文档
* @param params 参数
*/
private void replaceInTable(CustomXWPFDocument doc, Map<String, Object> params, List<String[]> tableList) {
Iterator<XWPFTable> iterator = doc.getTablesIterator();
XWPFTable table;
List<XWPFTableRow> rows;
List<XWPFTableCell> cells;
List<XWPFParagraph> paras;
while (iterator.hasNext()) {
table = iterator.next();
if (table.getRows().size() > 1) {
//判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
if (this.matcher(table.getText()).find()) {
rows = table.getRows();
for (XWPFTableRow row : rows) {
cells = row.getTableCells();
for (XWPFTableCell cell : cells) {
paras = cell.getParagraphs();
for (XWPFParagraph para : paras) {
this.replaceInPara(para, params, doc);
}
}
}
} else {
insertTable(table, tableList); //插入数据
}
}
}
} /**
* 正则匹配字符串
*
* @param str
* @return
*/
private Matcher matcher(String str) {
Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}", Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(str);
return matcher;
} /**
* 根据图片类型,取得对应的图片类型代码
*
* @param picType
* @return int
*/
private static int getPictureType(String picType) {
int res = CustomXWPFDocument.PICTURE_TYPE_PICT;
if (picType != null) {
if (picType.equalsIgnoreCase("png")) {
res = CustomXWPFDocument.PICTURE_TYPE_PNG;
} else if (picType.equalsIgnoreCase("dib")) {
res = CustomXWPFDocument.PICTURE_TYPE_DIB;
} else if (picType.equalsIgnoreCase("emf")) {
res = CustomXWPFDocument.PICTURE_TYPE_EMF;
} else if (picType.equalsIgnoreCase("jpg") || picType.equalsIgnoreCase("jpeg")) {
res = CustomXWPFDocument.PICTURE_TYPE_JPEG;
} else if (picType.equalsIgnoreCase("wmf")) {
res = CustomXWPFDocument.PICTURE_TYPE_WMF;
}
}
return res;
} // /**
// * 将输入流中的数据写入字节数组
// *
// * @param in
// * @return
// */
// public static byte[] inputStream2ByteArray(InputStream in, boolean isClose) {
// byte[] byteArray = null;
// try {
// int total = in.available();
// byteArray = new byte[total];
// in.read(byteArray);
// } catch (IOException e) {
// e.printStackTrace();
// } finally {
// if (isClose) {
// try {
// in.close();
// } catch (Exception e2) {
// e2.getStackTrace();
// }
// }
// }
// return byteArray;
// } /**
* 封装图片信息
* @return
*/
public static Map packageImgMessage(String imagePath){
Map map = new HashMap();
InputStream ips = null;
try {
ips = new FileInputStream(imagePath);
BufferedImage image = ImageIO.read(ips);
map.put("width", image.getWidth());
map.put("height", image.getHeight());
ips.close();
ips = new FileInputStream(imagePath);
map.put("type", imagePath.substring(imagePath.lastIndexOf('.')+1));
int total = ips.available();
byte[] byteArray = new byte[total];
ips.read(byteArray);
map.put("content", byteArray);
} catch (Exception e) {
e.printStackTrace();
}finally {
if (ips != null){
try {
ips.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return map;
} /**
* 关闭输入流
*
* @param is
*/
private void close(InputStream is) {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} /**
* 关闭输出流
*
* @param os
*/
private void close(OutputStream os) {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

  这样就实现了前台html转换为docx的word文档,并且将前台呈现的图片插入到生成的文档中。

  我想解决文档中的图片的初衷是因为导出的文档需要有饼图,如下所示:

  这个饼图是word文档做出来的,并不是图片,我使用了简单的方法使用图片展现的方式呈现出来。下篇文章将介绍生成饼图的实现方式。

params包含前台传回的html内容

最新文章

  1. chrome扩展程序开发
  2. Zabbix使用点滴
  3. 使用NSKeyedArchiver归档
  4. php 数组操作
  5. MongoDB与php的配合使用 【windows版】
  6. 1106. Two Teams(dfs 染色)
  7. 用java面向对象的思想实现的汉诺塔问题
  8. bootstrap-datetimepicker 时间表箭头不能显示
  9. USACO月赛数据
  10. 跳转表C语言,不比redis版本号
  11. Oracle trunc()函数的用法--来着心静禅定ing
  12. Unity3D调用摄像头
  13. TCP/IP中你不得不知的十大秘密
  14. Ubuntu修改系统时间
  15. java中父类和子类初始化顺序
  16. 分布式系统一致性协议--Paxos算法
  17. 解析 Spring ConversionService
  18. ios 在https情况下,使用webview加载url出错的解决方法 ios9 适配问题
  19. 探究react-native 源码的图片缓存
  20. 安全 流程服务器开新机器 内外网 iptables 安全组 用户安全root用户的使用.

热门文章

  1. sklearn学习笔记(1)--make_blobs函数及相应参数简介
  2. Mixin类的实现
  3. 全程2分钟!教你如何免费下载Windows 10
  4. 回收 PV【转】
  5. NCSC敦促开发者淘汰Python 2
  6. PHP截取指定字符前的字符串
  7. Java笔记--基础
  8. java面试题汇总,不断更新中。。。
  9. (九)微信小程序---for指令
  10. 中兴将用“加减乘除”建立理想 5G 网络