単語を生成するためのFreemarkテンプレートのアイデアはPOIのアイデアに似ていますが、Freemarkの機能はPOIよりも強力です。
POIについては、前の記事を参照してください:https://blog.csdn.net/CarryBest/article/details/94630824
1つ:最初にテンプレートを作成します
新しい単語テンプレートを作成し、その中に必要な形式を作成します。変数名を直接記述してxmlとして保存し、xml、Baiduを開いてデータをxml形式でフォーマットし、変数が混乱していないかどうかを確認します。めちゃくちゃ、手動で元に戻します。
最も基本的なのはこの使用法であり、変数をNullと判断できます。その他の処理
特定の変数をループさせたい場合は、<#list connmlist asconnms>を使用できます。。。。</#list>この種の記述、connmlistは、Javaバックエンドの変数connmsです。connmは、Javaバックエンドのデータ形式が次のようになっていることを意味します。対応する色でマークを付けることができます。
connmMap.put("connm", wqDataEditsData.getConnm());
connmList.add(connmMap);
resultMap.put( " connmlist "、connmList);
2:新しいパブリックメソッドDocumentHandler.javaを作成します
package com.XX.XXXX.documentHandler;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.Map;
/**
* 类描述:
*
* @author :carry
* @version: 1.0 CreatedDate in 2019年10月12日
* <p>
* 修订历史: 日期 修订者 修订描述
*/
public class DocumentHandler {
private Configuration configuration = null;
public DocumentHandler() {
configuration = new Configuration();
configuration.setDefaultEncoding("utf-8");
}
public void createDoc( Map<String ,Object > result,String fileName,String inPath,String outPath) throws Exception {
configuration.setDefaultEncoding("utf-8");
//模板方在项目的resource下的template里面
// configuration.setClassForTemplateLoading(this.getClass(), "/template");
//模板放在项目的盘符下
configuration.setDirectoryForTemplateLoading(new File(inPath));
System.out.println("文件路径"+outPath);
Template t = null;
try {
t = configuration.getTemplate(fileName,"utf-8");
} catch (IOException e) {
e.printStackTrace();
}
File outFile = new File(outPath);
Writer out = null;
try {
out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "utf-8"));
} catch (Exception e) {
e.printStackTrace();
}
try {
t.process(result, out);
} catch (TemplateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @方法描述:
* path 下载的文件路径 projectname 这里意义不大 直接被vue前台替换了 suffix后缀名 这里意义不大 直接被vue前台替换了
* @return: javax.servlet.http.HttpServletResponse
* @Author: carry
*/
public HttpServletResponse download(String downloadPath, HttpServletResponse response) throws Exception {
// 以流的形式下载文件。
InputStream fis = new BufferedInputStream(new FileInputStream(downloadPath));
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fis.close();
response.reset();
// 设置response的Header 这里意义不大 直接被vue前台替换了 所以可以删掉
response.addHeader("Content-Disposition", "attachment;filename="
+"test"+ new String(("" + ".docx").getBytes("gbk"),
"iso-8859-1"));
OutputStream toClient = new BufferedOutputStream(
response.getOutputStream());
//注意这里很重要 vue的回调里根据ContentType类判断是否是下载的操作,这里不要改
response.setContentType("application/octet-stream");
toClient.write(buffer);
toClient.flush();
this.close(fis);
this.close(toClient);
return response;
}
/**
* 关闭输入流
* @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();
}
}
}
}
3:クラスの呼び出し:
// 数据库里的数据与xml映射起来getLogsData这个方法就省略了,意思很清楚,就是你xml里有叫${name}的,那么java的map里也要响应的放一个键值对的键叫name的。
Map<String, Object> resultMap = this.getLogsData(params);
/**
* @方法描述: 下载导出整编数据
* @return: void
* @Author: carry
*/
@GetMapping("/exportToWordByFmk")
public void exportToWordByFmk(HttpServletRequest request,
HttpServletResponse response,
@RequestParam(value = "tm", required = true) String tm) {
try {
Map<String, String> params = new HashMap<String, String>();
params.put("tm", tm);
String path = ResourceUtils.getURL("classpath:").getPath();
//项目获取盘符
String root =path.substring(1,3);
//创建生成的目录
File outPathFile = new File(root+"\\jddsFile\\docResults");
if (!outPathFile.exists()) {
outPathFile.mkdirs();
}
//创建读取模板的目录
File inPathFile = new File(root+"\\jddsFile\\docTemplate");
if (!inPathFile.exists()) {
inPathFile.mkdirs();
}
//模板名称
String fileName = "WQDataEditsDataTmpFmk.xml";
//输出模板名字,java生成的文件名字,前台会被vue替换掉
String outPath =outPathFile.getAbsolutePath()+"\\WQDataEditsData.docx";
String inPath =inPathFile.getAbsolutePath();
// 讲数据库里的数据与xml映射起来
Map<String, Object> resultMap = this.getLogsData(params);
DocumentHandler dh = new DocumentHandler();
dh.createDoc(resultMap, fileName,inPath, outPath);
dh.download(outPath, response);
} catch (Exception e) {
e.printStackTrace();
}
}
4:vueの呼び出し
downloadFiles(row) {
let params = {};
this.$api.exportToWordByFmk(params).then(res => {
if (res.code == "0") {
let data = res.result.data;
let blob = new Blob([data], {
//type mimi类型 可以百度详细类型
//word文档为msword,application/pdfpdf文档为pdf application/vnd.ms-excel
//image/jpeg jpg
// type: `msword`
type: `application/msword`
});
let objectUrl = URL.createObjectURL(blob);
let link = document.createElement("a");
let fname = "测试";
link.href = objectUrl;
link.setAttribute("download", fname);
document.body.appendChild(link);
link.click();
} else {
this.$message.error(res.msg);
}
});
},
5:ファイルストリームを受信するカプセル化されたvueの方法
//ここで、バックエンドがファイルストリームの形式で戻る場合、カスタムリターンコンテンツは応答ファイルストリームにスプライスされます
contentType = headers ['content-type'];
if(contentType == "application / octet-stream"){
resultData = dataとします。
stdRs = {"code":0、 "msg": "操作成功"、 "result":{"data":resultData}}
}
//核心请求函数,并做拦截处理。
// config: {url,method,params,data,timeout,headers,message,loading}
// resp: {data:{},status:200,statusText:'OK',headers:{},config:{},request:{}}
request(config) {
this.onStart(config);
return this.http.request(config).then((res) => {
let {data, headers, request, status, statusText} = res;
let stdRs = null;
//此处如果后台返回的是文件流的形式,则自定义返回内容拼接上response的文件流
let contentType=headers['content-type'];
if(contentType=="application/octet-stream"){
let resultData=data;
stdRs={"code":0,"msg":"操作成功","result":{ "data":resultData}}
}else{
try {
stdRs = JSON.parse(data);
} catch (e) {
stdRs = this.stdRs(status + '', statusText, data);
}
}
this.onComplete(config, stdRs);
return this.resultSuccess(stdRs) ? Promise.resolve(stdRs) : Promise.reject(stdRs);
}).catch((err) => {
if (!err.response && !err.request) {
return Promise.reject(err);
}
let {request, response} = err;
let {status, statusText} = response;
let stdRs = this.stdRs(status + '', statusText, null);
this.onComplete(config, stdRs);
return Promise.reject(stdRs);
});
}
//导出文件
export(config) {
if (config.data) {
config.responseType="blob";
config.params = {...config.params, ...config.data};
if(config.data.params != undefined){
config.params={ ...config.params,...config.data.params}
}
delete config.data;
}
return this.request(config);
}