0、通过上一篇已经能够跑起来将ftl展示成html,这里进一步将ftl模版文件生成pdf和doc
1、公共类,初始化freemarker,从request里面获取参数,组装参数等
import freemarker.template.Configuration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
/**
* Created by Administrator on 2016/11/2.
*/
public class FreemarkerBase {
protected final Log logger = LogFactory.getLog(getClass());
private Configuration freemarker_cfg = null;
/**
* 获取freemarker的配置. freemarker本身支持classpath,目录和从ServletContext获取.
*/
protected Configuration getFreeMarkerCFG(){
if (null == freemarker_cfg){
freemarker_cfg = new Configuration();
freemarker_cfg.setDefaultEncoding("utf-8");
//ftl是放在classpath下的一个目录
freemarker_cfg.setClassForTemplateLoading(this.getClass(), "/freemarker/");
}
return freemarker_cfg;
}
//取出req中的参数,这里最好用guawa等做一下参数验证,否则会报错,展示不合理
protected Map getRequestParams(HttpServletRequest req, String type){
String imgPath = req.getSession().getServletContext().getRealPath("");
Map<String, Object> map = new HashMap<>();
Enumeration<String> paramNames = req.getParameterNames();
// 通过循环将表单参数放入键值对映射中
while(paramNames.hasMoreElements()) {
String key = paramNames.nextElement();
String value = req.getParameter(key);
//word:如果ftl绑定的图片,需要根目录的路径
if("doc".equals(type)
&& (value.toLowerCase().contains(".jpg")
|| value.toLowerCase().contains(".png"))){
value = req.getSession().getServletContext().getRealPath("/upload") + "/" + value;
}
//pdf:这里我写的是访问连接的根目录,因为不知道为什么我写全路径就不行,怪异了。
else if("pdf".equals(type)
&& (value.toLowerCase().contains(".jpg")
|| value.toLowerCase().contains(".png"))){
value = "http://localhost:8080/upload/" + value;
}
// System.out.println(value);
map.put(key, value);
}
return map;
}
}
2、公共方法
/*
*下面两个是doc和pdf的公共方法
*/
//生成临时文件方法
//templateFileName 模版名,即ftl文件名,见上面的show.ftl
//params ftl文件内的参数绑定
//htmlFilePath 文件生成目录
//suffix 后缀,eg:.doc .pdf 等等
private String createTemplateDownFile(String templateFileName, Map params,
String htmlFilePath, String suffix){
String htmlFileName = "temp" + (int) (Math.random() * 100000) + suffix;
try{
Template t = getFreeMarkerCFG().getTemplate(templateFileName);
File mkPath = new File(htmlFilePath);
if (!mkPath.exists()){
mkPath.mkdirs();
}
File file = new File(htmlFilePath + "/" + htmlFileName);
Writer out = new OutputStreamWriter(new FileOutputStream(file), "utf-8");
t.process(params, out);
}catch (TemplateException e){
logger.error("freemarker模版参数绑定错误 " + templateFileName,e);
e.printStackTrace();
}catch (IOException e){
logger.error("html生成失败 " + htmlFileName,e);
e.printStackTrace();
}
return htmlFileName;
}
//下载方法
private void downLoad(HttpServletResponse resp, String allPath, String suffix){
resp.setCharacterEncoding("utf-8");
//这里是一个生成名字的方法,我是用了一个我们自己的公共类,按照日期生成名字
String name = DateUtil.dateToStr(new Date(), 5 );
//根据后缀判断resp的题头文件
if("html".equals(suffix)){
resp.setContentType("text/html");
} else if("doc".equals(suffix)){
resp.setContentType("application/msword");
} else if("pdf".equals(suffix)){
resp.setContentType("application/PDF");
}
resp.addHeader("Content-Disposition", "attachment;filename=" + name + suffix);
//开始下载
File file = new File(allPath);
try {
InputStream fin = new FileInputStream(file);
ServletOutputStream out = resp.getOutputStream();
byte[] buffer = new byte[512]; // 缓冲区
int bytesToRead = -1;
// 通过循环将读入的文件的内容输出到浏览器中
while((bytesToRead = fin.read(buffer)) != -1) {
out.write(buffer, 0, bytesToRead);
}
} catch (IOException e) {
e.printStackTrace();
}
}
3、生成doc文件很简单,不用额外导入什么jar
//主方法
@RequestMapping("downDoc")
public void downDoc(HttpServletRequest request, HttpServletResponse resp){
String rootPath = request.getSession().getServletContext().getRealPath("/doc");
String fileName = createTemplateDownFile("show.ftl" , getRequestParams(request, "doc"), rootPath, ".doc");
String allPath = rootPath + "/" + fileName;
//下载公共方法
downLoad(resp, allPath, ".doc");
}
4、生成pdf文件,pdf我是通过ftl生成html文件,再通过这个静态的html文件生成pdf,这样不知道是不是冗余,当时是测试html的下载,所以直接拿来用了。
引入jar:
<dependency>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
<version>2.1.7</version>
</dependency>
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf</artifactId>
<version>9.0.9</version>
</dependency>
有歧义的jar,不引入
<!-- 2.*直接跳到了5.*,结构不大一样,网上demo多是2.*的,所以暂时先用了 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.4.3</version>
</dependency>
<!-- 我用bootstarp的css文件这里一直报错,后来用了上面的,是基于java,2.1css渲染的 -->
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>core-renderer</artifactId>
<version>R8</version>
</dependency>
<!-- 用了自己的文字,如果用itext的需要这个jar展示中文,不过我一直也搞不好报错,所以就用自己的了 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
代码:
/**
* 下载pdf文件
* @param request
* @param resp
*/
@RequestMapping("downPdf")
public void downPdf(HttpServletRequest request, HttpServletResponse resp){
//eg:c:/workspace/项目/doc/
String rootPath = request.getSession().getServletContext().getRealPath("");
String fileName = createTemplateDownFile("show.ftl" , getRequestParams(request, "pdf"), rootPath + "/html", ".html");
//根目录+下设文件夹名+文件名
String allPath = rootPath + "/html/" + fileName;
try {
//输入html的路径
String url = new File(allPath).toURI().toURL().toString();
//输出pdf路径,这里偷懒替换了html的名字改为pdf名字
String newFileName = fileName.substring(0, fileName.lastIndexOf(".")) + ".pdf";
String outputFile = rootPath + "/pdf/" + newFileName;
OutputStream os = new FileOutputStream(outputFile);
ITextRenderer renderer = new ITextRenderer();
renderer.setDocument(url);
// 解决中文支持问题,我是 copy了系统的宋体放到了项目里面,itext是直接扫描到classpath下面的,所以是相对路径,绝对也是可以的。还有,千万要注意,这里用simsun字体,也就是宋体,所以在ftl中的body标签要加样式<body style="font-family: SimSun">,而且ftl非常严格,meta标签最后一般没有“/>”,会报错提醒这里格式不对。
ITextFontResolver fontResolver = renderer.getFontResolver();
fontResolver.addFont("/font/simsun.ttc", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
// 解决图片的相对路径问题,因为从request中提取参数的时候已经加上了域名全路径,这里就不写文件全路径了
// renderer.getSharedContext().setBaseURL("file:/C:xxxx");
renderer.layout();
renderer.createPDF(os);
os.close();
//下载公共方法
downLoad(resp, outputFile, ".pdf");
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
}
}
5、最后
- 浏览器访问:localhost:8080/downPdf.do?host=qrcode.jpg或者localhost:8080/downDoc.do?host=qrcode.jpg就自动下载word和pdf文档了。
- 不知道能不能直接ftl生成pdf,网上相关资料也不多,正好这个html下载下来判断样式,就先这样吧。
- 还有一个问题是生成的pdf、html、doc因为要下载,所以都没有写删除的方法,暂时也不知道怎么清理。想到可以存入数据库,存入路径和名字需要的时候下载,还可以有删除的方法……再说。
- 这里是记录为主,图片的路径和jar的选择,样式的判断费了很大的劲,所以记录一下以防以后使用。
- 配合上一篇文章食用才可,springmvc的总体配置就不写了。
最后再重申一下,千万要注意,这里用simsun字体,也就是宋体,所以在ftl中的body标签要加样式style=”font-family: SimSun”,而且ftl非常严格,meta标签最后一般没有“/>”,会报错提醒这里格式不对。