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标签最后一般没有“/>”,会报错提醒这里格式不对。