Home

NUNU

13 Jun 2018

java使用模板生成word,java使用模板生成pdf

spring boot 项目想导出word文档。

使用技术: velocity

# pom.xml
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity</artifactId>
    <version>1.7</version>
</dependency>
  1. word 另存为xml。
  2. xml 用word打开,与源文件看着没有区别
  3. 编辑需要填写的字符。
时间:${date}
最大值:${max}
  1. 检查编写的需要替换的字符是否正常。使用sublime 打开xml, 如果我们编写的字符可以搜到,则正常。

代码

  1. VelocityUtil
import java.io.StringWriter;
import java.util.Map;

import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;

/**
 * 模板数据
 **/
public class VelocityUtil {

    public static String evaluateString(String template,
                                        Map<String, Object> data) throws Exception {
        VelocityContext context = new VelocityContext();
        data.entrySet().forEach(entry -> {
            context.put(entry.getKey(), entry.getValue());
        });
        StringWriter writer = new StringWriter();
        Velocity.evaluate(context, writer, "template", template);
        return writer.toString();
    }
}


  1. vm.vm
$rankMap.get("cq")
${rankMap.get("cq").get(0)}
${rankMap.get("cq").get(0).getValue()}
  1. ExportWord
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author: pnunu
 * @Date: Created in 10:03 2018/6/7
 * @Description: 导出word
 */
public class ExportWord {

    private static Logger logger = LoggerFactory.getLogger(ExportWord.class);

//
//    public static String export2(Map<String,Object> data) throws Exception {
//        File f = new File("E:\\word\\wm.vm");
//        String vmContext = FileUtils.readFileToString(f,"UTF-8");
//        String re = VelocityUtil.evaluateString(vmContext, data);
//        String filePathName = "E:\\word\\vm.txt";
//        writeFileTxt(re, filePathName);
//        return filePathName;
//    }

    public static String export(Map<String,Object> data, String filePathName) throws Exception {
        String xmlFile = "template/bdz/pre-bdz.xml";
        String vmContext = getXmlString(xmlFile);
        String re = VelocityUtil.evaluateString(vmContext, data);
        writeFileTxt(re, filePathName);
        return filePathName;
    }

    public static String getXmlString(String xmlName) {
        logger.info("xmlName==={}",xmlName);
        StringBuffer xml = new StringBuffer();
        try {
            InputStream is = ExportWord.class.getClassLoader().getResourceAsStream(xmlName);
            BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
            String line = "";
            while ((line = br.readLine()) != null) {
                xml.append(line);
            }
            return xml.toString();
        } catch (IOException e) {
            throw new MyException("无法找到要处理的文件,解析配置文件[" + xmlName + "]出错", e);
        }
    }


    public static void writeFileTxt(String re,String filePathName) throws Exception {
        File file = new File(filePathName).getParentFile();
        if (!file.exists()) {
            file.mkdirs();
        }
        FileOutputStream outSTr = new FileOutputStream(new File(filePathName));
        BufferedOutputStream buff = new BufferedOutputStream(outSTr);
        buff.write(re.getBytes("UTF-8"));
        buff.flush();
        buff.close();
        outSTr.close();
        logger.info("文件保存成功。保存路径是:" + filePathName);
    }
}

以上导出的word文件会存在文件。手机打开会显示源码。。。。。

导出真正的word

使用 freemarker

# pom.xml

<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.28</version>
</dependency>
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author: pnunu
 * @Date: Created in 17:47 2018/6/12
 * @Description:
 */
public class DownloadService {


    private Configuration configuration = null;

    public DownloadService() {
        configuration = new Configuration();
        configuration.setDefaultEncoding("UTF-8");
    }

    public static void main(String[] args) {
        DownloadService test = new DownloadService();
        test.createWord();
    }

    public void createWord() {
        Map<String, Object> dataMap = new HashMap<String, Object>();
        getData(dataMap);
        configuration.setClassForTemplateLoading(this.getClass(), "/");//模板文件所在路径
        Template t = null;
        try {
            t = configuration.getTemplate("pre-bdz.ftl"); //获取模板文件
        } catch (IOException e) {
            e.printStackTrace();
        }
        File outFile = new File("D:/outFile" + Math.random() * 10000 + ".doc"); //导出文件
        Writer out = null;
        try {
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile)));
        } catch (FileNotFoundException e1) {
            e1.printStackTrace();
        }
        try {
            t.process(dataMap, out); //将填充数据填入模板文件并输出到目标文件
        } catch (TemplateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void getData(Map<String, Object> dataMap) {
        dataMap.put("title", "标题");
        dataMap.put("nian", "2016");
        dataMap.put("yue", "3");
        dataMap.put("ri", "6");
        dataMap.put("shenheren", "lc");
        List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
        for (int i = 0; i < 10; i++) {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("xuehao", i);
            map.put("neirong", "内容" + i);
            list.add(map);
        }
        dataMap.put("list", list);
    }
}

至此一切没有问题。。。。。

客户说,我们想加个水印。。。

客户说,要不导出pdf吧,这样可以保护公司数据安全。。。

一万个不情愿,但依旧老老实实码代码。

解决方案:

  • 1、之前的word 转pdf

  • 2、放弃之前的word,直接导出pdf

方案1,实在可笑。但是还真有人这么干。

不建议这么做的一个重大原因是不能夸平台。。

使用jacob

import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.Dispatch;

import java.io.File;

/**
 * @Author: pnunu
 * @Date: Created in 19:26 2018/6/12
 * @Description:
 */
public class Word2PdfUtil {

    public static void main(String args[]) {
        ActiveXComponent app = null;
        String wordFile = "C:/Users/nunu/Desktop/word.doc";
        String pdfFile = "C:/Users/nunu/Desktop/pdf.pdf";
        System.out.println("开始转换...");
        // 开始时间
        long start = System.currentTimeMillis();
        try {
            // 打开word
            app = new ActiveXComponent("Word.Application");
            // 设置word不可见,很多博客下面这里都写了这一句话,其实是没有必要的,因为默认就是不可见的,如果设置可见就是会打开一个word文档,对于转化为pdf明显是没有必要的
            //app.setProperty("Visible", false);
            // 获得word中所有打开的文档
            Dispatch documents = app.getProperty("Documents").toDispatch();
            System.out.println("打开文件: " + wordFile);
            // 打开文档
            Dispatch document = Dispatch.call(documents, "Open", wordFile, false, true).toDispatch();
            // 如果文件存在的话,不会覆盖,会直接报错,所以我们需要判断文件是否存在
            File target = new File(pdfFile);
            if (target.exists()) {
                target.delete();
            }
            System.out.println("另存为: " + pdfFile);
            // 另存为,将文档报错为pdf,其中word保存为pdf的格式宏的值是17
            Dispatch.call(document, "SaveAs", pdfFile, 17);
            // 关闭文档
            Dispatch.call(document, "Close", false);
            // 结束时间
            long end = System.currentTimeMillis();
            System.out.println("转换成功,用时:" + (end - start) + "ms");
        }catch(Exception e) {
            System.out.println("转换失败"+e.getMessage());
            e.printStackTrace();
        }finally {
            // 关闭office
//            app.invoke("Quit", 0);
        }
    }


//    public static void main(String[] args) {
//        doc2pdf("C:/Users/nunu/Desktop/word.doc", "C:/Users/nunu/Desktop/pdf.pdf");
//    }

//  此方式有水印,不建议使用
//    public static boolean getLicense() {
//        boolean result = false;
//        try {
////            InputStream is = Word2PdfUtil.class.getClassLoader().getResourceAsStream("license.xml"); // license.xml应放在..\WebRoot\WEB-INF\classes路径下
////            License aposeLic = new License();
////            aposeLic.setLicense();
//            result = true;
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        return result;
//    }
//
//    public static void doc2pdf(String inPath, String outPath) {
//        if (!getLicense()) { // 验证License 若不验证则转化出的pdf文档会有水印产生
//            return;
//        }
//        try {
//            long old = System.currentTimeMillis();
//            File file = new File(outPath); // 新建一个空白pdf文档
//            FileOutputStream os = new FileOutputStream(file);
//            Document doc = new Document(inPath); // Address是将要被转化的word文档
//            doc.save(os, SaveFormat.PDF);// 全面支持DOC, DOCX, OOXML, RTF HTML, OpenDocument, PDF,
//            // EPUB, XPS, SWF 相互转换
//            long now = System.currentTimeMillis();
//            System.out.println("共耗时:" + ((now - old) / 1000.0) + "秒"); // 转化用时
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }
}

jacob 导出的结果简直完美。跟手动点击另存为的结果一模一样。但是他依赖于office的基础控件,且不能兼容Linux,部署很麻烦

最终解决方案 — 直接导出pdf

使用技术: itextpdf

# pom.xml
<!-- https://mvnrepository.com/artifact/com.itextpdf/itextpdf -->
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>5.5.10</version>
</dependency>
  • 实现方案1
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.*;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * @Author: pnunu
 * @Date: Created in 23:12 2018/6/12
 * @Description: pdf
 */
public class PdfUtil {

    // 利用模板生成pdf
    public static void fillTemplate() {
        // 模板路径
        String templatePath = "E:/word/pdf/ceshi.pdf";
        // 生成的新文件路径
        String newPDFPath = "E:/word/pdf/out.pdf";
        PdfReader reader;
        FileOutputStream out;
        ByteArrayOutputStream bos;
        PdfStamper stamper;
        try {
            out = new FileOutputStream(newPDFPath);// 输出流
            reader = new PdfReader(templatePath);// 读取pdf模板
            bos = new ByteArrayOutputStream();
            stamper = new PdfStamper(reader, bos);
            AcroFields form = stamper.getAcroFields();

            String[] str = {"123456789", "TOP__ONE", "男", "1991-01-01", "130222111133338888", "河北省保定市"};
            int i = 0;
            java.util.Iterator<String> it = form.getFields().keySet().iterator();
            while (it.hasNext()) {
                String name = it.next().toString();
                System.out.println(name);
                form.setField(name, name + "00");
            }
            stamper.setFormFlattening(true);// 如果为false那么生成的PDF文件还能编辑,一定要设为true
            stamper.close();

            Document doc = new Document();
            PdfCopy copy = new PdfCopy(doc, out);
            doc.open();
            PdfImportedPage importPage = copy.getImportedPage(new PdfReader(bos.toByteArray()), 1);
            copy.addPage(importPage);
            doc.close();

        } catch (IOException e) {
            System.out.println(1);
        } catch (DocumentException e) {
            System.out.println(2);
        }

    }

    public static void main(String[] args) {
        fillTemplate();
    }
}

  • 实现2
import com.itextpdf.text.Document;
import com.itextpdf.text.Font;
import com.itextpdf.text.pdf.*;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.net.URL;
import java.util.Map;
import java.util.Set;

/**
 * @Author: pnunu
 * @Date: Created in 23:25 2018/6/12
 * @Description: pdf 生成
 */
public class PdfUtils {
    // 利用模板生成pdf
    public static void pdfOut(Map<String, Object> dateMap, String newPDFPath) {
        PdfReader reader;
        FileOutputStream out;
        ByteArrayOutputStream bos;
        PdfStamper stamper;
        try {
//            BaseFont bf = BaseFont.createFont(ttfURL.getPath(), BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
            BaseFont bf = BaseFont.createFont("font/simhei.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
            Font FontChinese = new Font(bf, 5, Font.NORMAL);
            out = new FileOutputStream(newPDFPath);// 输出流
            reader = new PdfReader(PdfUtils.class.getClassLoader().getResourceAsStream("template/bdz/pre-bdz.pdf"));// 读取pdf模板
            bos = new ByteArrayOutputStream();
            stamper = new PdfStamper(reader, bos);
            AcroFields form = stamper.getAcroFields();
            //文字类的内容处理
            form.addSubstitutionFont(bf);
//            for (String key : dateMap.keySet()) {
////                Object value = dateMap.get(key);
//                String value = VelocityUtil.evaluateString(key, dateMap);
//
//                form.setField(key, value);
//            }
            Set<String> keySet = form.getFields().keySet();
            for (String key : keySet) {
                String value = VelocityUtil.evaluateString(key, dateMap);

                form.setField(key, value);
            }
            stamper.setFormFlattening(true);// 如果为false,生成的PDF文件可以编辑,如果为true,生成的PDF文件不可以编辑
            stamper.close();
            Document doc = new Document();
//            Font font = new Font(bf, 32);
            PdfCopy copy = new PdfCopy(doc, out);
            doc.open();
            PdfImportedPage importPage = copy.getImportedPage(new PdfReader(bos.toByteArray()), 1);
            copy.addPage(importPage);
            doc.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

完美解决问题

【参考文章】 1、 https://blog.csdn.net/top__one/article/details/65442390 2、 https://www.cnblogs.com/wangpeng00700/p/8418594.html


业精于勤,荒于嬉; 行成于思,毁于随。
pnunu at 22:46

Flag Counter