Java利用POI实现对Word的操作(包括有换行的文本和图片)

目录

一、实现效果

二、实现部分

1、导入依赖

2、工具类

3、实体类

4、测试代码


如果您也有类似的需求,可以参考这篇文章进行实现并扩展。

一、实现效果

1、重要说明:

①普通文本使用 ${字段名} 进行标注,有换行的文本使用 $${字段名} 进行标注。

②图片使用 #{字段名} 进行标注。

③${ }、$${ }、#{ } 相当于一个占位符,需要在其他地方编辑好然后一整块复制进来word文档,否则会出现不识别为一个整体的情况!!(建议在电脑版微信的聊天框里输入好后再复制到word文档,我是这样做的)

2、操作前:

3、操作后:

补充:为什么要特意加一类占位符,来表示有换行的字符串?

这是因为在Java里面字符串中的 \n ,插入word时显示的效果只是有一个空格。 

有换行的文本在word里面使用 $${字段名} 标注,在Java代码里面跟普通文本一样,Map的key值使用 ${字段名} 即可,这是为了方便用工具类生成替换文本的Map时,不需要特殊处理。

二、实现部分

1、导入依赖

		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi</artifactId>
			<version>3.16</version>
		</dependency>
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-ooxml</artifactId>
			<version>3.16</version>
		</dependency>

2、工具类

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.lang.reflect.Field;
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;

import javax.imageio.ImageIO;

public class WordUtil {

    /**
     * 用于直接根据对象生成需要替换的文本Map:${属性名}和值
     *
     * @param obj
     * @return
     */
    public static Map<String, String> getTextFieldMap(Object obj) {
        Map<String, String> map = new HashMap<>();
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            // 如果字段是私有的,也需要访问
            field.setAccessible(true);
            try {
                String value = (String) field.get(obj);
                map.put("${" + field.getName() + "}", Objects.nonNull(value) ? value : "     ");
            } catch (IllegalAccessException e) {
                System.out.println("字段解析失败:" + e);
            }
        }
        return map;
    }

    /**
     * 根据模板生成新word文档
     *
     * @param inputUrl  模板存放地址
     * @param outputUrl 新文档存放地址
     * @param textMap   需要替换的文本信息集合
     * @param imgMap    需要替换的图片路径集合
     */
    public static void wordToNewWord(String inputUrl, String outputUrl, Map<String, String> textMap,
                                     Map<String, List<String>> imgMap) throws Exception {
        if (Objects.isNull(textMap)) {
            textMap = new HashMap<>();
        }
        if (Objects.isNull(imgMap)) {
            imgMap = new HashMap<>();
        }
        try (FileInputStream fileInputStream = new FileInputStream(inputUrl)) {
            XWPFDocument document = new XWPFDocument(fileInputStream);
            // 解析替换文本段落对象
            WordUtil.changeParagraphs(document.getParagraphs(), textMap, imgMap);
            // 解析替换表格对象
            WordUtil.changeTables(document.getTables(), textMap, imgMap);
            // 生成新的word
            try (FileOutputStream stream = new FileOutputStream(outputUrl)) {
                document.write(stream);
            }
        }
    }

    /**
     * 解析所有段落,将占位符替换成具体的文本/图片
     *
     * @param paragraphs 所有表格集合
     * @param textMap    需要替换的文本集合
     * @param imgMap     需要替换的图片路径(本地)集合
     * @throws Exception
     */
    public static void changeParagraphs(List<XWPFParagraph> paragraphs,
                                        Map<String, String> textMap, Map<String, List<String>> imgMap) throws Exception {
        for (XWPFParagraph paragraph : paragraphs) {
            String text = paragraph.getText();
            // 检查是否需要替换
            if (!containsTextPlaceholder(text) && !containsImgPlaceholder(text)) {
                continue;
            }
            List<XWPFRun> runs = paragraph.getRuns();
            for (XWPFRun run : runs) {
                // 是否需要:占位符 $${}-->有换行的文本
                if (textMap.containsKey(run.toString().substring(1))) {
                    changeRun(run, textMap.get(run.toString().substring(1)));
                }
                // 是否需要:占位符 ${}-->文本
                if (textMap.containsKey(run.toString())) {
                    run.setText(textMap.get(run.toString()), 0);
                }
                // 是否需要:占位符 #{}-->图片
                if (imgMap.containsKey(run.toString())) {
                    setCellImg(run, imgMap.get(run.toString()));
                }
            }
        }
    }

    /**
     * 解析所有表格,将占位符替换成具体的文本/图片
     *
     * @param tables  所有表格集合
     * @param textMap 需要替换的文本集合
     * @param imgMap  需要替换的图片路径(本地)集合
     * @throws Exception
     */
    public static void changeTables(List<XWPFTable> tables,
                                    Map<String, String> textMap,
                                    Map<String, List<String>> imgMap) throws Exception {
        // 获取表格对象集合
        for (XWPFTable table : tables) {
            // 判断表格内容是否需要替换
            if (!containsTextPlaceholder(table.getText()) && !containsImgPlaceholder(table.getText())) {
                continue;
            }
            // 需要替换,则遍历表格
            List<XWPFTableRow> rows = table.getRows();
            for (XWPFTableRow row : rows) {
                List<XWPFTableCell> cells = row.getTableCells();
                for (XWPFTableCell cell : cells) {
                    // 判断单元格是否需要替换
                    if (!containsTextPlaceholder(cell.getText()) && !containsImgPlaceholder(cell.getText())) {
                        continue;
                    }
                    List<XWPFParagraph> paragraphs = cell.getParagraphs();
                    changeParagraphs(paragraphs, textMap, imgMap);
                }
            }
        }
    }

    /**
     * 处理有换行的文本
     *
     * @param run
     * @param text
     * @throws Exception
     */
    private static void changeRun(XWPFRun run, String text) throws Exception {
        run.setText("", 0);
        // 如果内容为空,则插入TAB占位
        if (text == null) {
            run.addTab();
        }
        // 处理有换行的内容
        String[] texts = text.split("\n");
        for (int i = 0; i < texts.length-1; i++){
            run.setText(texts[i]);
            run.addCarriageReturn();
        }
        // 最后一个不需要换行
        run.setText(texts[texts.length-1]);
    }

    /**
     * 批量插入多张图片,一张一行
     *
     * @param run
     * @param urls
     * @throws Exception
     */
    private static void setCellImg(XWPFRun run, List<String> urls) throws Exception {
        // 清空占位符内容
        run.setText("", 0);
        // 数字格式化器:保留0位小数、向上取整
        NumberFormat numberFormat = NumberFormat.getNumberInstance();
        numberFormat.setMaximumFractionDigits(0);
        numberFormat.setRoundingMode(RoundingMode.UP);

        if (Objects.isNull(urls) || urls.isEmpty()) {
            return;
        }
        // 循环插入图片
        for (String path : urls) {
            File imageFile = new File(path);
            //判断图片是否存在
            if (!imageFile.exists()) {
                continue;
            }
            // 获取图片格式
            int format = getImageFormat(path);
            if (format == 0) {
                continue;
            }
            // 读取图片
            try (FileInputStream is = new FileInputStream(imageFile)) {
                // 计算适合文档宽高的图片EMU数值
                BufferedImage read = ImageIO.read(imageFile);
                int width = Units.toEMU(read.getWidth());
                int height = Units.toEMU(read.getHeight());
                // 1 EMU = 1/914400英寸= 1/36000 mm,15是word文档中图片能设置的最大宽度cm
                double scaleFactor = width / 360000.0 / 14.5d;
                width = Integer.parseInt(numberFormat.format(width / scaleFactor).replace(",", ""));
                height = Integer.parseInt(numberFormat.format(height / scaleFactor).replace(",", ""));
                // 插入图片
                run.addPicture(is, format, imageFile.getName(), width, height);
            }
            // 换行
            run.addBreak();
        }
    }

    /**
     * 获取图片格式
     *
     * @param path 图片路径
     * @return 图片格式枚举数字
     */
    private static int getImageFormat(String path) {
        int format = 0;
        if (path.endsWith(".emf")) {
            format = XWPFDocument.PICTURE_TYPE_EMF;
        } else if (path.endsWith(".wmf")) {
            format = XWPFDocument.PICTURE_TYPE_WMF;
        } else if (path.endsWith(".pict")) {
            format = XWPFDocument.PICTURE_TYPE_PICT;
        } else if (path.endsWith(".jpeg") || path.endsWith(".jpg")) {
            format = XWPFDocument.PICTURE_TYPE_JPEG;
        } else if (path.endsWith(".png")) {
            format = XWPFDocument.PICTURE_TYPE_PNG;
        } else if (path.endsWith(".dib")) {
            format = XWPFDocument.PICTURE_TYPE_DIB;
        } else if (path.endsWith(".gif")) {
            format = XWPFDocument.PICTURE_TYPE_GIF;
        } else if (path.endsWith(".tiff")) {
            format = XWPFDocument.PICTURE_TYPE_TIFF;
        } else if (path.endsWith(".eps")) {
            format = XWPFDocument.PICTURE_TYPE_EPS;
        } else if (path.endsWith(".bmp")) {
            format = XWPFDocument.PICTURE_TYPE_BMP;
        } else if (path.endsWith(".wpg")) {
            format = XWPFDocument.PICTURE_TYPE_WPG;
        }
        return format;
    }

    /**
     * 判断文本中是否包含: ${
     */
    private static boolean containsTextPlaceholder(String text) {
        return text.contains("${");
    }

    /**
     * 判断文本中是否包含: #{
     */
    private static boolean containsImgPlaceholder(String text) {
        return text.contains("#{");
    }
}

3、实体类

public class User {
    private String name;
    private String gender;
    private String phone;
    private String address;
    private String province;
    private String city;

    public User(String name, String gender, String phone, String address, String province, String city) {
        this.name = name;
        this.gender = gender;
        this.phone = phone;
        this.address = address;
        this.province = province;
        this.city = city;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }
}

4、测试代码

import org.junit.jupiter.api.Test;

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

public class TestWordUtil {
    @Test
    public void test() {
        String inputUrl = "D:\\test-poi-word.docx";
        String outputUrl = "D:\\new-poi-word.docx";
        User user = new User("张三", "男", "12345678901", "广东省广州市\n这是具体地址", "广东省", "广州市");
        // 使用工具类生成字段与值的映射,也可以自己手动写,对于字段多的这样比较方便
        Map<String, String> textMap = WordUtil.getTextFieldMap(user);
        // 需要插入的图片
        Map<String, List<String>> imgMap = new HashMap<>();
        imgMap.put("#{image}", List.of("D:\\test-poi-word1.jpeg","D:\\test-poi-word2.jpg"));
        try {
            WordUtil.wordToNewWord(inputUrl, outputUrl, textMap, imgMap);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值