工作中经常有时会遇到需要给用户创建word文档并实现word文档在线预览 的需求,我来讲一讲我所知道的解决方案。
一、word文档模板定义
因word文档中内容是根据不同用户显示不一样,所以需要使用较为灵活的模板了,本文使用的是freemarker+xml
来定义模板:
1. maven依赖:
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.20</version>
</dependency>
2.word文档准备
需要准备好一份word文档,内容已充填好,.doc格式文档
类似这种:
填写完成后需另存为.xml格式文档,需要注意,图片会已base64编码保存下来,需要把这个xml中的图片替换成可配置的参数;找到contentType="image/jpeg"
的标记,替换base64图片的内容为:${image1}/${image2}
另外,${}标签中不要包含空格,否则会被word给分隔,形成的文档会无法识别这个替换符号
二、word文档创建
模板准备好后,需要使用freemarker来生成word
具体方法如下:
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import sun.misc.BASE64Encoder;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
@Slf4j
public class WordUtil {
private static final String ENCODING = "UTF-8";
private static Configuration cfg = new Configuration();
public static String CURRENT_DIR = System.getProperty("user.dir").replace('\\', '/');
public static String FREEMARKER_PATH = "freemarker";
public static String NOT_FOUND_IMG = CURRENT_DIR + File.separator + FREEMARKER_PATH + File.separator + "404.jpg";
//初始化cfg
static {
//设置模板所在文件夹
try {
cfg.setDirectoryForTemplateLoading(new File(CURRENT_DIR + File.separator + FREEMARKER_PATH));
} catch (IOException e) {
log.error("load freemarker error 404");
}
// setEncoding这个方法一定要设置国家及其编码,不然在ftl中的中文在生成html后会变成乱码
cfg.setEncoding(Locale.getDefault(), ENCODING);
// 设置对象的包装器
cfg.setObjectWrapper(new DefaultObjectWrapper());
// 设置异常处理器,这样的话就可以${a.b.c.d}即使没有属性也不会出错
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
}
//获取模板对象
public static Template getTemplate(String templateFileName) throws IOException {
return cfg.getTemplate(templateFileName, ENCODING);
}
/**
* 据数据及模板生成文件
*
* @param data Map的数据结果集
* @param templateFileName ftl模版文件名
* @param outFilePath 生成文件名称(可带路径)
*/
public static File crateFile(Map<String, Object> data, String templateFileName, String outFilePath) {
Writer out = null;
File outFile = new File(outFilePath);
try {
// 获取模板,并设置编码方式,这个编码必须要与页面中的编码格式一致
Template template = getTemplate(templateFileName);
if (!outFile.getParentFile().exists()) {
outFile.getParentFile().mkdirs();
}
out = new OutputStreamWriter(new FileOutputStream(outFile), ENCODING);
// 处理模版
template.process(data, out);
out.flush();
} catch (Exception e) {
log.error("模板生成文件失败:{}, {}, {}", outFilePath, templateFileName, e.getMessage());
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
log.error("关闭Write对象出错", e);
}
}
return outFile;
}
//获得图片的base64码
public static String getImageBase(String src) throws Exception {
if (StringUtils.isBlank(src)) {
return "";
}
InputStream in;
byte[] data = null;
if (src.startsWith("http")) {
URL url = new URL(src);
if (StringUtils.isBlank(url.getFile())) {
return "";
}
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(3000);
connection.setDoInput(true);
connection.setRequestMethod("GET");
int code = connection.getResponseCode();
if (code == 200) {
in = new BufferedInputStream(connection.getInputStream());
} else {
log.error("request {} error", src);
return "";
}
} else {
File file = new File(src);
if (!file.exists()) {
return "";
}
in = new FileInputStream(file);
}
try {
data = new byte[in.available()];
in.read(data);
in.close();
} catch (IOException e) {
log.error(e.getMessage());
}
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(data);
}
public static void main(String[] args) throws Exception {
Map<String, Object> data = new HashMap<>();
data.put("userId", 1);
data.put("username", "test");
data.put("phone", "110");
data.put("image1", getImageBase("C:\\Users\\Public\\Pictures\\Sample Pictures\\139268975275.JPG"));
data.put("image2", getImageBase("C:\\Users\\Public\\Pictures\\Sample Pictures\\139268975275.JPG"));
crateFile(data, "model.xml", "C:\\Users\\pc02\\Desktop\\sql\\result.doc");
}
}
手动调用main方法就可以创建word了,创建的word如图:
三、word在线预览方案
word文档大家也知道,浏览器是无法识别在线查看的,一般都需要下载下来使用本地的办公软件打开,所以需要使用第三方工具进行在线预览,推荐永中文档在线预览,方便快捷
1.申请应用
进入永中云服务网站: https://open.yozocloud.cn
点击产品- 文档云预览
进入后先申请一个云预览的appId和密钥,审批通过后就可以调用api接口实现在线预览的功能了
2.上传文档
先调用接口上传文件到服务器中: http://dmc.yozocloud.cn/api/ file/upload?appId=xxxx&sign=xxxx
调用成功后会返回一个文件版本信息:
{
"data": {
"fileVersionId": "fileVersionId", //文件的版本id
"fileId": "fileId" //文件id
},
"message": "操作成功",
"errorcode": 0
}
3.查看预览效果
通过使用这个fileVersionId就可以查看预览页面 http://eic.yozocloud.cn/api/view/file?fileVersionId=xxxx&appId=xxxx&sign=xxxxx
在浏览器中输入这个拼接好的地址就可以直接重定向到在线查看这个html页面了 效果如下:
这样就可以给用户在线查看所需要的word文档了