FreeMarker导出Word(带图片、解决图片变形问题)

思想说明:
        本文内容的核心:使用FreeMarker的模板引擎技术,导出word。


总体步骤:

第一步:创建一个word模板,里面的对应位置使用FreeMarker的占位符表示。

注:为了导出的word兼容2003版本,我们创建的word模板,最好是doc后缀的。

注:本人用的是wps,office应该也是一样的(这个没试过)。

第二步:将该word,另存为xml文件。

提示:也可以第二步时先用一个特别的字符占位,然后在第三步时,再在xml中将对应的字符替换为FreeMarker能识别
           的占位符。

第三步:打开该xml文件,检查占位符是否"变形"

提示:本人是以安装了indent XML插件的sublime打开的。

注:有时,另存为xml时,占位符可能会变形(如:“${age}”变为“${ag</w:t></w:r></w:p></w:tc>e}”),这就会导致FreeMarker
       不能识别,那么就需要我们手动调整或者重复第二步重新生成,直到所有的占位符都正确。

第四步:创建一个项目,并引入FreeMarker的jar包。

<!--FreeMarker -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

注:本人创建的是maven项目(SpringBoot),直接在pom.xml中引入FreeMarker的依赖即可。

第五步:将xml文件,修改后缀为(freemarker模板的).ftl,并放入项目classpath下
               的templates目录下。

第六步:编写导出word工具类

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import sun.misc.BASE64Encoder;

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

/**
 * freemarker + word模板 导出word
 *
 * @author JustryDeng
 * @date 2018/11/14 17:41
 */
public class FreemarkeExportrWordUtil {

    /** 默FreeMarker配置实例 */
    private static final Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);

    /** 默认采用UTF-8编码 */
    private static final String ENCODING = "UTF-8";

    /** buffer */
    private static final int BUFFER_SIZE = 1024;

    /**
     * 从指定freemarker模板所在文件夹
     *
     * “/”            对应 classpath
     * “/templates/”  对应 classpath下的templates/
     */
    private static final String DEFAULT_POSITION = "/";

    static {
        configuration.setDefaultEncoding(ENCODING);
    }

    /**
     * 导出excel
     *
     * @param templateFileName
     *         模板文件名(含后缀,如:abc.ftl)
     * @param resultFileAllPathName
     *         结果文件全路径文件名 (如: C:/Users/result.doc  再如: C:/Users/result.docx)
     * @param dataObject
     *         与模板中的占位符 对应的 数据信息(一般为:一个专门创建的对象, 或者是Map)
     * @return 生成的word文件
     * @throws IOException
     * @throws TemplateException
     * @date 2018/11/16 10:52
     */
    public static File doExport(String templateFileName, String resultFileAllPathName, Object dataObject)
                                throws IOException, TemplateException {
        return doExport(templateFileName, DEFAULT_POSITION, resultFileAllPathName, dataObject);
    }

    /**
     * 导出excel
     *
     * @param templateFileName
     *         模板文件名(含后缀,如:abc.ftl)
     * @param templateFileDir
     *         模板文件所在位置名(如: "/" 代表 classpath)
     * @param resultFileAllPathName
     *         结果文件全路径文件名 (如: C:/Users/result.doc  再如: C:/Users/result.docx)
     * @param dataObject
     *         与模板中的占位符 对应的 数据信息(一般为:一个专门创建的对象, 或者是Map)
     * @return 生成的word文件
     * @throws IOException
     * @throws TemplateException
     * @date 2018/11/16 10:52
     */
    public static File doExport(String templateFileName, String templateFileDir,
                                String resultFileAllPathName, Object dataObject)
                                throws IOException, TemplateException {

        // 指定模板文件所在  位置
        configuration.setClassForTemplateLoading(FreemarkeExportrWordUtil.class, templateFileDir);

        // 根据模板文件、编码;获取Template实例
        Template template = configuration.getTemplate(templateFileName, ENCODING);

        File resultFile = new File(resultFileAllPathName);
        // 判断要生成的word文件所在目录是否存在,不存在则创建
        if (!resultFile.getParentFile().exists()) {
            boolean result = resultFile.getParentFile().mkdirs();
        }
        // 写出文件
        try (OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(resultFile));
             Writer writer = new BufferedWriter(osw, BUFFER_SIZE)) {
            template.process(dataObject, writer);
        }
        return resultFile;
    }

    /**
     * 获取图片对应的base64码
     *
     * @param imgFile
     *         图片
     * @return 图片对应的base64码
     * @throws IOException
     * @date 2018/11/16 17:05
     */
    public static String getImageBase64String(File imgFile) throws IOException {
        InputStream inputStream = new FileInputStream(imgFile);
        byte[] data = new byte[inputStream.available()];
        int totalNumberBytes = inputStream.read(data);
        BASE64Encoder encoder = new BASE64Encoder();
        return encoder.encode(data);
    }

}

第七步:使用工具类导出word即可


使用测试:

测试示例一:基本测试

测试主函数为:

public static void main(String[] args) {
        // 模板文件名
        String templateFileName = "exportWordDemoTwo.ftl";
        // 模板文件所在位置
        String templateFileDir = "/templates/";
        // 要生成的文件 全路径文件名
        String fileName = "C:/Users/JustryDeng/Desktop/result.doc";

        // 组装FreeMarker占位符对应的数据
        Map<String, Object> dataMap = new HashMap<>(8);
        dataMap.put("year", "2018");
        dataMap.put("month", 11);
        dataMap.put("day", 16);
        dataMap.put("name", "JustryDeng");
        dataMap.put("age", 24);
        dataMap.put("handsomeValue", 100);
        dataMap.put("isSingle", "是");
        try {
            FreemarkeExportrWordUtil.doExport(templateFileName, templateFileDir, fileName, dataMap);
        } catch (IOException | TemplateException e) {
            e.printStackTrace();
        }
    }

执行主函数后,在指定位置就生成了对应的文件:

打开该文件:

测试示例二:集合数据测试

我们回到第二步、第三步;在xml中添加对应的freemarker标签:

其他的不变。这次将该xml后缀名改为ftl,这次放入classpath下(这样做纯粹是为了使用一下工具类中的另一个方法):

测试主函数为:

public static void main(String[] args) {
        // 模板文件名
        String templateFileName = "exportWordDemoTwo.ftl";
        // 要生成的文件 全路径文件名
        String fileName = "C:/Users/JustryDeng/Desktop/result123.doc";

        // 组装FreeMarker占位符对应的数据
        // 组装FreeMarker占位符对应的数据
        Map<String, Object> dataMap = new HashMap<>(8);
        dataMap.put("year", "2018");
        dataMap.put("month", 11);
        dataMap.put("day", 16);
        List<Map<String, Object>> list = new ArrayList<>(8);
        dataMap.put("personList", list);
        for (int i = 0; i < 10; i++) {
            Map<String, Object> tempMap = new HashMap<>(8);
            tempMap.put("name", "JustryDeng");
            tempMap.put("age", 24);
            tempMap.put("handsomeValue", 90 + i);
            tempMap.put("isSingle", "是");
            list.add(tempMap);
        }
        try {
            FreemarkeExportrWordUtil.doExport(templateFileName, fileName, dataMap);
        } catch (IOException | TemplateException e) {
            e.printStackTrace();
        }
    }

执行主函数后,在指定位置就生成了对应的文件:

打开该文件:

测试示例三:导出图片测试

同样的,先在word中放一张图片:

然后在第三步时,将xml中该图片对应的base64码替换为freemarker对应的占位符:

替换结果如图所示:

其他的不变。将该xml后缀名改为ftl,并将模板文件放入classpath下:

测试主函数为:

public static void main(String[] args) {
        // 模板文件名
        String templateFileName = "exportWordDemoThree.ftl";

        // 要生成的文件 全路径文件名
        String fileName = "C:/Users/JustryDeng/Desktop/resultImage.doc";

        // 组装FreeMarker占位符对应的数据
        Map<String, Object> dataMap = new HashMap<>(8);
        dataMap.put("year", "2018");
        dataMap.put("month", 11);
        dataMap.put("day", 16);
        dataMap.put("name", "JustryDeng");
        dataMap.put("age", 24);
        dataMap.put("handsomeValue", 100);
        dataMap.put("isSingle", "是");
        try {
            File imageFile = new File("C:/Users/JustryDeng/Desktop/66.png");
            dataMap.put("myImage", FreemarkeExportrWordUtil.getImageBase64String(imageFile));
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            FreemarkeExportrWordUtil.doExport(templateFileName, fileName, dataMap);
        } catch (IOException | TemplateException e) {
            e.printStackTrace();
        }
    }

执行主函数后,在指定位置就生成了对应的文件:

打开该文件:

注:实际上,导出的word里面的图片,宽高还是原来模板图片的大小;也就是说:这可能导致导出的word里面的图片出
     现变形。


解决图片变形:

指定图片的宽高:

在xml中对应设置宽高的位置,使用freemarker占位符:

在程序中指定图片的宽高:

public static void main(String[] args) {
        // 模板文件名
        String templateFileName = "exportWordDemoThree.ftl";

        // 要生成的文件 全路径文件名
        String fileName = "C:/Users/JustryDeng/Desktop/resultImage.doc";

        // 组装FreeMarker占位符对应的数据
        Map<String, Object> dataMap = new HashMap<>(8);
        dataMap.put("year", "2018");
        dataMap.put("month", 11);
        dataMap.put("day", 16);
        dataMap.put("name", "JustryDeng");
        dataMap.put("age", 24);
        dataMap.put("handsomeValue", 100);
        dataMap.put("isSingle", "是");
        try {
            File imageFile = new File("C:/Users/JustryDeng/Desktop/66.png");
            BufferedImage bufferedImage = ImageIO.read(imageFile);
            // 单位为 px
            int width = bufferedImage.getWidth();
            int height = bufferedImage.getHeight();
            // 设置图片的大小
            dataMap.put("width", width + "px");
            dataMap.put("height", height + "px");
            dataMap.put("myImage", FreemarkeExportrWordUtil.getImageBase64String(imageFile));
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            FreemarkeExportrWordUtil.doExport(templateFileName, fileName, dataMap);
        } catch (IOException | TemplateException e) {
            e.printStackTrace();
        }
    }

 

提示:由于freemarker模板走的其实是XML,所以如果想输出类似于“>“、“<”这样的XML敏感的字符的话,
            需要将“<”写为“&lt;”、“>”写为“&gt;”,如:

 

^_^ 如有不当之处,欢迎指正
^_^ 本文已经被收录进《程序员成长笔记(三)》,笔者JustryDeng

  • 8
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
### 回答1: Freemarker可以通过使用Apache POI库来导出图片Word文档。首先,需要在模板定义一个图片占位符,然后在Java代码图片插入到占位符。具体步骤如下: 1. 在Word模板定义一个图片占位符,例如:${image}。 2. 在Java代码,使用Apache POI库加载模板文件,并获取到模板图片占位符。 3. 使用POI的XWPFRun类创建一个新的段落,并将图片插入到段落。 4. 将段落插入到模板图片占位符位置。 5. 保存生成的Word文档。 示例代码如下: ``` // 加载模板文件 FileInputStream fis = new FileInputStream("template.docx"); XWPFDocument doc = new XWPFDocument(fis); // 获取图片占位符 XWPFParagraph imagePlaceholder = doc.getParagraphs().stream() .filter(p -> p.getText().contains("${image}")) .findFirst().orElse(null); // 创建新的段落 XWPFParagraph newParagraph = doc.createParagraph(); // 插入图片到段落 XWPFRun newRun = newParagraph.createRun(); newRun.addPicture(new FileInputStream("image.jpg"), XWPFDocument.PICTURE_TYPE_JPEG, "image.jpg", Units.toEMU(200), Units.toEMU(200)); // 将段落插入到图片占位符位置 int index = doc.getPosOfParagraph(imagePlaceholder); doc.removeBodyElement(index); doc.setParagraph(newParagraph, index); // 保存生成的Word文档 FileOutputStream fos = new FileOutputStream("output.docx"); doc.write(fos); fos.close(); ``` 注意:在使用POI插入图片,需要指定图片的类型和大小。示例代码使用的图片类型为JPEG,大小为200x200像素。 ### 回答2: Freemarker是一种模板引擎,可以将数据和模板结合生成静态文本,并且常见在Spring框架使用。而导出Word图片,一般需要通过POI或者Apache POI来实现,具体步骤如下: 1.首先需要引入POI和Freemarker的jar包。 2.在模板添加图片占位符,例如${logo}。 3.通过Java代码将图片读入到输出流,然后在模板替换${logo}的内容为图片字节数组的Base64编码。 4.通过Freemarker将数据和模板结合,生成Word文件。 5.最后需要使用POI将Word文件的后缀名由.ftl改为.doc或.docx,并且编写下载逻辑进行下载。 需要注意的是,在将图片插入Word文档,可能出现图片比例失调或者无法插入图片的情况,这需要调整插入图片的方式,可以将图片插入一个模板,然后将模板插入到Word文档,以保证插入的图片比例正确。 ### 回答3: FreeMarker是一种Java模板引擎,它允许使用模板生成文本输出,其包括MS Word文档。在导出word图片,需要使用FreeMarker的JDBC模式来检索数据和图片,然后将它们插入Word文档。以下是导出Word图片的步骤: 1.准备Word模板:首先需要创建一个Word模板,包含需要添加文本的区域和占位符来插入图片。这可以通过在Word创建一个新文档,添加文本和占位符,然后保存为docx文件来完成。 2.准备模板数据:使用Java代码从数据库检索需要导出的数据和图片,并将它们作为数据模型引入FreeMarker模板引擎。 3.将数据模型应用于模板:使用FreeMarker模板引擎将数据模型应用于Word模板,并生成将要输出的Word文档。 4.插入图片:通过在FreeMarker模板使用图片占位符,将图片插入到生成的Word文档。这可以通过将图片从数据库检索出来并使用二进制方式插入模板来完成。 5.保存Word文档:完成所有文本和图片的插入后,将生成的Word文档保存到文件系统或输出流即可。 在使用FreeMarker导出Word图片,需要注意以下几点: 1.在Word模板应该包含正确的占位符并设置样式和布局。 2.从数据库检索图片应该压缩和缩放图片以确保它们适合文档的区域。 3.在插入图片,正确处理可能出现的图片格式和分辨率问题。 4.应对可能出现的异常情况进行处理,以确保生成的Word文档具有良好的稳定性和可靠性。 总之,使用FreeMarker导出Word图片是一项需要谨慎处理的复杂任务。通过遵循以上步骤和注意事项,可以生成具有高质量和稳健性的Word文档并满足客户的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值