java后台生成统计报告word
技术方案:通过生成echart所需的json格式,调用cmd命令 phantomjs 生成统计图片,用dom4j的方式插入到word中
话不多说,直接看demo
package com.goodteacher.echart;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;
import org.apache.poi.util.IOUtils;
import org.docx4j.TraversalUtil;
import org.docx4j.dml.wordprocessingDrawing.Inline;
import org.docx4j.finders.RangeFinder;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.Body;
import org.docx4j.wml.CTBookmark;
import org.docx4j.wml.Document;
import org.docx4j.wml.Drawing;
import org.docx4j.wml.ObjectFactory;
import org.docx4j.wml.P;
import org.docx4j.wml.R;
import com.github.abel533.echarts.axis.CategoryAxis;
import com.github.abel533.echarts.axis.ValueAxis;
import com.github.abel533.echarts.code.Tool;
import com.github.abel533.echarts.code.Trigger;
import com.github.abel533.echarts.json.GsonOption;
import com.github.abel533.echarts.series.Line;
import com.google.gson.Gson;
public class EchartGenerate {
private static final String JSpath = "C:\\Users\\mayn\\Desktop\\echart-java\\echarts-convert\\echarts-convert.js";
public static void main(String[] args) throws Exception {
//生成图形所需json
//String optiona = generateLine();
String optiona = "{\"title\":{\"text\":\"电流图\",\"subtext\":\"电流图\",\"x\":\"left\"},\"toolbox\":{\"feature\":{\"saveAsImage\":{\"show\":true,\"title\":\"保存为图片\",\"type\":\"png\",\"lang\":[\"点击保存\"]}},\"show\":true},\"tooltip\":{\"trigger\":\"axis\"},\"legend\":{\"data\":[\"邮件营销\",\"联盟广告\",\"视频广告\"]},\"xAxis\":[{\"type\":\"category\",\"boundaryGap\":false,\"data\":[\"周一\",\"周二\",\"周三\",\"周四\",\"周五\",\"周六\",\"周日\"]}],\"yAxis\":[{\"type\":\"value\"}],\"series\":[{\"name\":\"邮件营销\",\"type\":\"line\",\"stack\":\"总量\",\"data\":[120,132,101,134,90,230,210]},{\"name\":\"联盟广告\",\"type\":\"line\",\"stack\":\"总量\",\"data\":[220,182,191,234,290,330,310]},{\"name\":\"视频广告\",\"type\":\"line\",\"stack\":\"总量\",\"data\":[150,232,201,154,190,330,410]}]}";
String imagePath = generateEChart(optiona);
System.out.println("统计图:&&&&" + imagePath);
String newWordPath = createWord(imagePath);
System.out.println("生成的word文件:******" + newWordPath);
}
/**
* 折线图
*
* isHorizontal 是否水平放置
*/
public static String generateLine() {
String[] types = {"邮件营销", "联盟广告", "视频广告"};
int[][] datas = {{120, 132, 101, 134, 90, 230, 210}, {220, 182, 191, 234, 290, 330, 310}, {150, 232, 201, 154, 190, 330, 410}};
String title = "电流图";
String[] valXis = {"周一", "周二", "周三", "周四", "周五", "周六", "周日"};
GsonOption option = new GsonOption();
option.title().text(title).subtext("电流图").x("left");// 大标题、小标题、位置
// 提示工具
option.tooltip().trigger(Trigger.axis);// 在轴上触发提示数据
// 工具栏
option.toolbox().show(true).feature(Tool.saveAsImage);// 显示保存为图片
option.legend(types);// 图例
CategoryAxis category = new CategoryAxis();// 轴分类
category.data(valXis);
category.boundaryGap(false);// 起始和结束两端空白策略
// 循环数据
for (int i = 0; i < types.length; i++) {
Line line = new Line();// 三条线,三个对象
String type = types[i];
line.name(type).stack("总量");
for (int j = 0; j < datas[i].length; j++)
line.data(datas[i][j]);
option.series(line);
}
option.xAxis(category);// x轴
option.yAxis(new ValueAxis());// y轴
return new Gson().toJson(option);
}
@SuppressWarnings("deprecation")
public static String createWord(String imagePath) throws Exception {
// 模板文件路径
String templatePath = "D:/temp/Echart/template.docx";
// 生成的文件路径
String targetPath = "D:/temp/Echart/target.docx";
// 书签名
String bookmarkName = "bookmark";
// 图片路径
//String imagePath = "image.jpg";
// 载入模板文件
WordprocessingMLPackage wPackage = WordprocessingMLPackage.load(new FileInputStream(templatePath));
// 提取正文
MainDocumentPart mainDocumentPart = wPackage.getMainDocumentPart();
Document wmlDoc = (Document) mainDocumentPart.getJaxbElement();
Body body = wmlDoc.getBody();
// 提取正文中所有段落
List<Object> paragraphs = body.getContent();
// 提取书签并创建书签的游标
RangeFinder rt = new RangeFinder("CTBookmark", "CTMarkupRange");
new TraversalUtil(paragraphs, rt);
// 遍历书签
for (CTBookmark bm:rt.getStarts()) {
// 这儿可以对单个书签进行操作,也可以用一个map对所有的书签进行处理
if (bm.getName().equals(bookmarkName)){
// 读入图片并转化为字节数组,因为docx4j只能字节数组的方式插入图片
InputStream is = new FileInputStream(imagePath);
byte[] bytes = IOUtils.toByteArray(is);
// 穿件一个行内图片
BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wPackage, bytes);
// 最后一个是限制图片的宽度,缩放的依据
Inline inline = imagePart.createImageInline(null, null, 0,1, false, 10000);
// 获取该书签的父级段落
P p = (P)(bm.getParent());
ObjectFactory factory = new ObjectFactory();
// R对象是匿名的复杂类型,然而我并不知道具体啥意思,估计这个要好好去看看ooxml才知道
R run = factory.createR();
// drawing理解为画布?
Drawing drawing = factory.createDrawing();
drawing.getAnchorOrInline().add(inline);
run.getContent().add(drawing);
p.getContent().add(run);
}
}
wPackage.save(new FileOutputStream(targetPath));
return targetPath;
}
@SuppressWarnings("finally")
public static String generateEChart(String options) {
String dataPath = writeFile(options);
String fileName= "test-"+UUID.randomUUID().toString().substring(0, 8) + ".png";
String path = "D:\\temp\\Echart\\" +fileName;
try {
File file = new File(path); //文件路径(路径+文件名)
if (!file.exists()) { //文件不存在则创建文件,先创建目录
File dir = new File(file.getParent());
dir.mkdirs();
file.createNewFile();
}
String cmd = "phantomjs " + JSpath + " -infile " + dataPath + " -outfile " + path;
Runtime.getRuntime().exec(cmd);//生成图片
/*Process process = Runtime.getRuntime().exec(cmd);
BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = "";
while ((line = input.readLine()) != null) {
System.out.println(line);
}
input.close();*/
} catch (IOException e) {
e.printStackTrace();
}finally{
return path;
}
}
public static String writeFile(String options) {
String dataPath="D:\\chartData\\data"+ UUID.randomUUID().toString().substring(0, 8) +".json";
try {
/* 写入Txt文件 */
File writename = new File(dataPath); // 相对路径,如果没有则要建立一个新的output.txt文件
if (!writename.exists()) { //文件不存在则创建文件,先创建目录
File dir = new File(writename.getParent());
dir.mkdirs();
writename.createNewFile(); // 创建新文件
}
BufferedWriter out = new BufferedWriter(new FileWriter(writename));
out.write(options); // \r\n即为换行
out.flush(); // 把缓存区内容压入文件
out.close(); // 最后记得关闭文件
} catch (IOException e) {
e.printStackTrace();
}
return dataPath;
}
}
所需jar maven
<dependency>
<groupId>org.docx4j</groupId>
<artifactId>docx4j</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>com.github.abel533</groupId>
<artifactId>ECharts</artifactId>
<version>3.0.0.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-excelant</artifactId>
<version>3.12</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>3.12</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.8</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>3.8</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-exec</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
好!
以上,完美解决、