La idea de la plantilla Freemark para generar palabras es similar a la idea de POI, pero la función de Freemark es más poderosa que POI.
Para PDI, consulte el artículo anterior: https://blog.csdn.net/CarryBest/article/details/94630824
Uno: primero haz una plantilla
Cree una nueva plantilla de Word y cree el formato requerido en ella. Puede escribir el nombre de la variable directamente, luego guardarlo como xml, abrir xml, Baidu para formatear los datos en formato xml, y luego ver si sus variables están desordenadas, si en mal estado, cámbialo manualmente.
El más básico es este uso, puede juzgar la variable como nula y otros procesos
Si desea repetir una determinada variable, puede usar <#list connmlist como connms>. . . . </ # list> Este tipo de escritura, connmlist es una variable de su backend java, connms. connm significa que su formato de datos de backend java es así: el color correspondiente le ayuda a marcarlo
connmMap.put("connm", wqDataEditsData.getConnm());
connmList.add(connmMap);
resultMap.put (" connmlist ", connmList );
Dos: crea un nuevo método público 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();
}
}
}
}
Tres: clase de llamada:
// 数据库里的数据与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();
}
}
Cuatro: llamada de 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);
}
});
},
Cinco: el método de transmisión de archivos de recepción de vue encapsulada
// Aquí, si el backend regresa en forma de flujo de archivo, el contenido de retorno personalizado empalmado en el flujo de archivo de respuesta
let contentType = headers ['tipo de contenido'];
if (contentType == "application / octet-stream") {
dejar resultData = datos;
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);
}