写图片到word中
前言
在上一篇中已经生成了Jpg的echarts图片,在很久之前写了一个根据模板写word的工具代码,
工具代码,https://www.cnblogs.com/weiyanei/p/17040015.html
下面代码是定义工具代码中表格的行为,
//每个表格多少行表头
public static int[] headCount = new int[]{1,1,1};
//每个表格列数
public static int[] cellsCount = new int[]{6,7,8};
工具类修改
将以前的工具类替换下面的方法,增加了插入图片的代码。
/**
* 替换段落文本
* @param document docx解析对象
* @param textMap 需要替换的信息集合
*/
public static void changeText(XWPFDocument document, Map<String, String> textMap){
//获取段落集合
List<XWPFParagraph> paragraphs = document.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
//判断此段落时候需要进行替换
String text = paragraph.getText();
if(checkText(text)){
List<XWPFRun> runs = paragraph.getRuns();
for (XWPFRun run : runs) {
//替换模板原来位置
String value = changeValue(run.toString(), textMap);
if(value.indexOf("img|")>=0){
value = value.split("\\|")[1];
//插入图片
try {
run.setText("",0);
run.addPicture(new FileInputStream(value),XWPFDocument.PICTURE_TYPE_JPEG,"", Units.toEMU(400),Units.toEMU(200));
} catch (InvalidFormatException|IOException e) {
log.error("",e);
}
}else{
run.setText(value,0);
}
}
}
}
}
/**
* 匹配传入信息集合与模板
* @param value 模板需要替换的区域
* @param textMap 传入信息集合
* @return 模板需要替换区域信息集合对应值
*/
public static String changeValue(String value, Map<String, String> textMap){
Set<Entry<String, String>> textSets = textMap.entrySet();
for (Entry<String, String> textSet : textSets) {
//匹配模板与替换值 格式${key}
String keyName = textSet.getKey();
String key = "${"+keyName+"}";
if(value.indexOf(key)!= -1){
//增加特殊处理,判断是否哟啊插入图片
if(keyName.indexOf("img")>=0){//说明此处要插入图片
String picPath = textSet.getValue();//图片路径
value = "img|"+picPath;
}else{
value = textSet.getValue();
}
}
}
//模板未匹配到区域替换为空
if(checkText(value)){
value = "";
}
return value;
}
word中插入图片使用,
run.addPicture(new FileInputStream(value),XWPFDocument.PICTURE_TYPE_JPEG,"", Units.toEMU(400),Units.toEMU(200))
根据上述代码可以知道,主要是识别是否有${img}这个key,所以如果有多张图片,就直接使用 ${img01},${img02},只要里面是img开头就行,所以img就是图片的特定字段。
img对应的value就是图片的文件地址,即上一篇中返回的图片地址。
模板也放在resources下的files即可,如下图
使用
生成图片的方法是异步的,因为可能会耗点时,所以在生成图片时用来干点其他,不至于阻塞了。
/**
* 生成图表图片
*/
private FutureTask<PageData> createPic(Map<String,FactoryInfo> factoryMap){
FutureTask<PageData> future =
new FutureTask<>(new Callable<PageData>() {
public PageData call() {
try {
return echartsService.generateBarImage(packageBarOption(factoryMap));
} catch (Exception e) {
logger.error("", e);
return RespUtil.getErrorResp(RespUtil.SYSTEM_ERROR_CODE,RespUtil.SYSTEM_ERROR_MSG);
}
}
});
executorService.execute(new Runnable() {
@Override
public void run() {
future.run();
}
});
return future;
}
在主方法中调用
//创建图表
boolean hasPic = false;
FutureTask<PageData> future = this.createPic(factoryMap);
String picPath = this.getClass().getClassLoader().getResource("files").getPath()+File.separator+"bar.jpg";
try {
PageData pageData = future.get();
if(pageData.getString("code").equals("0")){
hasPic = true;
}
if(hasPic){//图片已经产生
map.put("desc_image","本月各工厂样品管理信息,如下图:");
picPath = pageData.getString("picPath"); //获取图片路径
map.put("img",picPath);
}
} catch (InterruptedException | ExecutionException e) {
logger.error("",e);
}
String inputUrl = "model.docx";
//创建word
boolean hasWord = false;
String reportName = "ats_report_"+huibaoMonthStr+"-"+UUID.randomUUID().toString().replaceAll("-","")+".docx";
try{
String path = this.getClass().getClassLoader().getResource("files/"+inputUrl).getPath();
String filePath = URLDecoder.decode(path, "UTF-8");
logger.info(filePath);
inputUrl = filePath;
String outPath = this.getClass().getClassLoader().getResource("files").getPath();
outPath += File.separator+reportName;
WordToNewWordUtils.changWord(inputUrl, outPath, map, list);
hasWord = true;
}catch (IOException e){
logger.error("",e);
}
执行创建word后就可以在工程下看到有docx文件生成,
如果不想生成docx文件,直接下载的话,那么只需要重载工具类的方法,把文件输出改为输出文件流就行,如下是重载方法,
public static boolean changWord(String inputUrl, OutputStream fileOutputStream,
Map<String, String> textMap, List<List<List<String>>> tableList) {
//模板转换默认成功
boolean changeFlag = true;
nowTableIndex = -1;
try {
//获取docx解析对象
XWPFDocument document = new XWPFDocument(POIXMLDocument.openPackage(inputUrl));
//解析替换文本段落对象
WordToNewWordUtils.changeText(document, textMap);
//解析替换表格对象
WordToNewWordUtils.changeTable(document, textMap, tableList);
//生成新的word
document.write(fileOutputStream);
} catch (IOException e) {
log.error("生成word文档:",e);
changeFlag = false;
}
return changeFlag;
}
使用的话如下所示,response是 HttpServletResponse,就是http的请求输出流,这样就实现文件下载了。
try{
String path = this.getClass().getClassLoader().getResource("files/"+inputUrl).getPath();
String filePath = URLDecoder.decode(path, "UTF-8");
log.info(filePath);
inputUrl = filePath;
response.setContentType("application/msword;charset=utf-8");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(outputFileName, "UTF-8"));
OutputStream outputStream = response.getOutputStream();
WordToNewWordUtils.changWord(inputUrl, outputStream, modelMap, list);
}catch (IOException e){
log.error("",e);
}
完整的工具代码
package com.staryea.util;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.poi.POIXMLDocument;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import java.io.*;
import java.util.*;
import java.util.Map.Entry;
public class WordToNewWordUtils {
protected static Log log = LogFactory.getLog(WordToNewWordUtils.class);
//每个表格多少行表头
public static int[] headCount = new int[]{1,1,1};
//每个表格列数
public static int[] cellsCount = new int[]{6,7,8};
private static int nowTableIndex = -1;
/**
* 根据模板生成新word文档
* 判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
* @param inputUrl 模板存放地址
* @param outputUrl 新文档存放地址
* @param textMap 需要替换的信息集合
* @param tableList 需要插入的表格信息集合
* @return 成功返回true,失败返回false
*/
public static boolean changWord(String inputUrl, String outputUrl,
Map<String, String> textMap, List<List<List<String>>> tableList) {
//模板转换默认成功
boolean changeFlag = true;
nowTableIndex = -1;
try {
//获取docx解析对象
XWPFDocument document = new XWPFDocument(POIXMLDocument.openPackage(inputUrl));
//解析替换文本段落对象
WordToNewWordUtils.changeText(document, textMap);
//解析替换表格对象
WordToNewWordUtils.changeTable(document, textMap, tableList);
//生成新的word
File file = new File(outputUrl);
FileOutputStream stream = new FileOutputStream(file);
document.write(stream);
stream.close();
} catch (IOException e) {
log.error("生成word文档:",e);
changeFlag = false;
}
return changeFlag;
}
public static boolean changWord(String inputUrl, OutputStream fileOutputStream,
Map<String, String> textMap, List<List<List<String>>> tableList) {
//模板转换默认成功
boolean changeFlag = true;
nowTableIndex = -1;
try {
//获取docx解析对象
XWPFDocument document = new XWPFDocument(POIXMLDocument.openPackage(inputUrl));
//解析替换文本段落对象
WordToNewWordUtils.changeText(document, textMap);
//解析替换表格对象
WordToNewWordUtils.changeTable(document, textMap, tableList);
//生成新的word
document.write(fileOutputStream);
} catch (IOException e) {
log.error("生成word文档:",e);
changeFlag = false;
}
return changeFlag;
}
/**
* 替换段落文本
* @param document docx解析对象
* @param textMap 需要替换的信息集合
*/
public static void changeText(XWPFDocument document, Map<String, String> textMap){
//获取段落集合
List<XWPFParagraph> paragraphs = document.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
//判断此段落时候需要进行替换
String text = paragraph.getText();
if(checkText(text)){
List<XWPFRun> runs = paragraph.getRuns();
for (XWPFRun run : runs) {
//替换模板原来位置
String value = changeValue(run.toString(), textMap);
if(value.indexOf("img|")>=0){
value = value.split("\\|")[1];
//插入图片
try {
run.setText("",0);
run.addPicture(new FileInputStream(value),XWPFDocument.PICTURE_TYPE_JPEG,"", Units.toEMU(400),Units.toEMU(200));
} catch (InvalidFormatException|IOException e) {
log.error("",e);
}
}else{
run.setText(value,0);
}
}
}
}
}
/**
* 替换表格对象方法
* @param document docx解析对象
* @param textMap 需要替换的信息集合
* @param tableList 需要插入的表格信息集合
*/
public static void changeTable(XWPFDocument document, Map<String, String> textMap,
List<List<List<String>>> tableList){
//获取表格对象集合
List<XWPFTable> tables = document.getTables();
for (int i = 0; i < tables.size(); i++) {
//只处理行数大于等于2的表格,且不循环表头
XWPFTable table = tables.get(i);
if(table.getRows().size()>1){
//判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
if(checkText(table.getText())){
List<XWPFTableRow> rows = table.getRows();
//遍历表格,并替换模板
eachTable(rows, textMap);
}else{
// System.out.println("插入"+table.getText());
List<List<String>> oneTableContent = tableList.get(i);
nowTableIndex++;
insertTable(table, oneTableContent);
}
}
}
}
/**
* 遍历表格
* @param rows 表格行对象
* @param textMap 需要替换的信息集合
*/
public static void eachTable(List<XWPFTableRow> rows ,Map<String, String> textMap){
for (XWPFTableRow row : rows) {
List<XWPFTableCell> cells = row.getTableCells();
for (XWPFTableCell cell : cells) {
//判断单元格是否需要替换
if(checkText(cell.getText())){
List<XWPFParagraph> paragraphs = cell.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
List<XWPFRun> runs = paragraph.getRuns();
for (XWPFRun run : runs) {
run.setText(changeValue(run.toString(), textMap),0);
}
}
}
}
}
}
/**
* 为表格插入数据,行数不够添加新行
* @param table 需要插入数据的表格
* @param tableList 插入数据集合
*/
public static void insertTable(XWPFTable table, List<List<String>> tableList){
if(tableList==null||tableList.size()==0){
return;
}
XWPFTableRow modelRow = table.getRow(headCount[nowTableIndex]);
//创建行,根据需要插入的数据添加新行,不处理表头
for(int i = 1; i < tableList.size(); i++){
XWPFTableRow row =table.createRow();
// XWPFTableRow row = table.insertNewTableRow(headCount[nowTableIndex]+1);
//创建行,根据第一行创建的,导致表头合并单位格影响了列数
List<XWPFTableCell> cells = row.getTableCells();
int minusCell = cellsCount[nowTableIndex]-cells.size();
for (int j = 0; j < minusCell; j++) {
row.addNewTableCell();
}
//行属性复制
row.getCtRow().setTrPr(modelRow.getCtRow().getTrPr());
cells = row.getTableCells();
for (int i1 = 0; i1 < cells.size(); i1++) {
XWPFTableCell modelCell = modelRow.getCell(i1);
//列属性复制
cells.get(i1).getCTTc().setTcPr(modelCell.getCTTc().getTcPr());
//段落属性复制
if(modelCell.getParagraphs()!=null&&modelCell.getParagraphs().size()>0){
cells.get(i1).getParagraphs().get(0).getCTP().setPPr(modelCell.getParagraphs().get(0).getCTP().getPPr());
}
}
}
//遍历表格插入数据
List<XWPFTableRow> rows = table.getRows();
int contentIndex = 0;
for(int i = headCount[nowTableIndex]; i < rows.size(); i++,contentIndex++){
XWPFTableRow newRow = table.getRow(i);
List<XWPFTableCell> cells = newRow.getTableCells();
for(int j = 0; j < cells.size(); j++){
XWPFTableCell cell = cells.get(j);
try{
cell.setText(tableList.get(contentIndex).get(j));
}catch (Exception e){
log.info(contentIndex+":"+nowTableIndex+":"+rows.size()+":"+cells.size());
}
}
}
}
/**
* 判断文本中时候包含$
* @param text 文本
* @return 包含返回true,不包含返回false
*/
public static boolean checkText(String text){
boolean check = false;
if(text.indexOf("$")!= -1){
check = true;
}
return check;
}
/**
* 匹配传入信息集合与模板
* @param value 模板需要替换的区域
* @param textMap 传入信息集合
* @return 模板需要替换区域信息集合对应值
*/
public static String changeValue(String value, Map<String, String> textMap){
Set<Entry<String, String>> textSets = textMap.entrySet();
for (Entry<String, String> textSet : textSets) {
//匹配模板与替换值 格式${key}
String keyName = textSet.getKey();
String key = "${"+keyName+"}";
if(value.indexOf(key)!= -1){
//增加特殊处理,判断是否哟啊插入图片
if(keyName.indexOf("img")>=0){//说明此处要插入图片
String picPath = textSet.getValue();//图片路径
value = "img|"+picPath;
}else{
value = textSet.getValue();
}
}
}
//模板未匹配到区域替换为空
if(checkText(value)){
value = "";
}
return value;
}
public static void main(String[] args) {
//模板文件地址
String inputUrl = "E:\\xyhj\\document\\2023-02\\ATS开发\\pic.docx";
//新生产的模板文件
Random random = new Random();
String outputUrl = "E:\\xyhj\\document\\2023-02\\ATS开发\\test"+random.nextInt(20)+".docx";
Map<String, String> testMap = new HashMap<String, String>();
testMap.put("img", "E:\\xyhj\\document\\2023-02\\ATS开发\\pic.jpg");
List<List<List<String>>> testList = new ArrayList<>();
WordToNewWordUtils.changWord(inputUrl, outputUrl, testMap, testList);
}
}