记录-poi文本替换,图片替换,转PDF

poi文本替换

  1. word文档中添加字段
    ${字段名}

模板字段
2. 创建文件副本,类型变更为zip格式
从zip的/word目录中取出document.xml文件
zip结构
3. jar包导入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.26-incubating</version>
</dependency>
  1. 代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
     /**
    * 根据模板生成docx文件
    * @param wordCommonDto 数据
    * @param fileName 文件名
    * @throws IOException
    * @throws TemplateException
    */
    public void makeWordByXcode(WordCommonDto wordCommonDto,String fileName) throws IOException, TemplateException {
    /** 初始化配置文件 **/
    Configuration configuration = new Configuration();
    String fileDirectory = basePath+wordCommonDto.getWordType()+"/";
    /** 加载文件 **/
    configuration.setDirectoryForTemplateLoading(new File(fileDirectory));
    /** 加载模板 **/
    Template template = configuration.getTemplate("document.xml");
    /** 准备数据 **/
    Map<String,String> dataMap = new HashMap<String, String>();
    /** 将数据映射为实体类后转为json格式,再转为map 避免字段值为null**/
    // dataMap.put("phone","1");
    // dataMap.put("year","2");
    // dataMap.put("month","3");
    // dataMap.put("day","4");
    String str = JSONObject.toJSONString(wordCommonDto, SerializerFeature.WriteNullStringAsEmpty);
    dataMap = JSONObject.parseObject(str,Map.class);
    log.info(dataMap.toString());
    /** 指定输出word文件的路径 **/

    String outFilePath = outPath+fileName+".xml";
    File docFile = new File(outFilePath);
    FileOutputStream fos = new FileOutputStream(docFile);
    Writer out = new BufferedWriter(new OutputStreamWriter(fos),10240);
    template.process(dataMap,out);
    if(out != null){
    out.close();
    }
    try {
    ZipInputStream zipInputStream = ZipUtils.wrapZipInputStream(new FileInputStream(new File(fileDirectory+wordCommonDto.getWordType()+".zip")));
    ZipOutputStream zipOutputStream = ZipUtils.wrapZipOutputStream(new FileOutputStream(new File(outPath+fileName+".docx")));
    String itemname = "word/document.xml";
    ZipUtils.replaceItem(zipInputStream, zipOutputStream, itemname, new FileInputStream(new File(outPath+fileName+".xml")));
    log.info("success");

    } catch (Exception e) {
    log.info(e.toString());
    }
    FileUtil.deleteFile(outPath+fileName+".xml");
    }
    需要的zip工具类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.util.zip.ZipEntry;
    import java.util.zip.ZipInputStream;
    import java.util.zip.ZipOutputStream;


    public class ZipUtils {
    /**
    * 替换某个 item,
    * @param zipInputStream zip文件的zip输入流
    * @param zipOutputStream 输出的zip输出流
    * @param itemName 要替换的 item 名称
    * @param itemInputStream 要替换的 item 的内容输入流
    */
    public static void replaceItem(ZipInputStream zipInputStream,
    ZipOutputStream zipOutputStream,
    String itemName,
    InputStream itemInputStream
    ){
    //
    if(null == zipInputStream){return;}
    if(null == zipOutputStream){return;}
    if(null == itemName){return;}
    if(null == itemInputStream){return;}
    //
    ZipEntry entryIn;
    try {
    while((entryIn = zipInputStream.getNextEntry())!=null)
    {
    String entryName = entryIn.getName();
    ZipEntry entryOut = new ZipEntry(entryName);
    // 只使用 name
    zipOutputStream.putNextEntry(entryOut);
    // 缓冲区
    byte [] buf = new byte[8*1024];
    int len;

    if(entryName.equals(itemName)){
    // 使用替换流
    while((len = (itemInputStream.read(buf))) > 0) {
    zipOutputStream.write(buf, 0, len);
    }
    } else {
    // 输出普通Zip流
    while((len = (zipInputStream.read(buf))) > 0) {
    zipOutputStream.write(buf, 0, len);
    }
    }
    // 关闭此 entry
    zipOutputStream.closeEntry();

    }
    } catch (IOException e) {
    e.printStackTrace();
    }finally {
    //e.printStackTrace();
    close(itemInputStream);
    close(zipInputStream);
    close(zipOutputStream);
    }
    }
    /**
    * 包装输入流
    */
    public static ZipInputStream wrapZipInputStream(InputStream inputStream){
    ZipInputStream zipInputStream = new ZipInputStream(inputStream);
    return zipInputStream;
    }
    /**
    * 包装输出流
    */
    public static ZipOutputStream wrapZipOutputStream(OutputStream outputStream){
    ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);
    return zipOutputStream;
    }
    private static void close(InputStream inputStream){
    if (null != inputStream){
    try {
    inputStream.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
    private static void close(OutputStream outputStream){
    if (null != outputStream){
    try {
    outputStream.flush();
    outputStream.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
    }

图片替换

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
* docx文档中图片替换
* @param file1 图片
* @param contractno 文件名
* @throws IOException
*/
public void replaceImages(File file1,String contractno) throws IOException {

InputStream docis = new FileInputStream(outPath+contractno+".docx");
String imagePosition = getImagePosition();
//转成word
XWPFDocument document = new XWPFDocument(docis);
try {
FileInputStream in = new FileInputStream(file1);
//得到输入流
//这里的rId4可不是固定的,这个其实就是图片在word文件中的标识,下方会给出获取的方法
document.getPackagePart().removeRelationship(imagePosition);
byte[] ba = new byte[in.available()];
int len = in.read(ba);
ByteArrayInputStream byteInputStream = new ByteArrayInputStream(ba, 0, len);
//设置图片
String ind =document.addPictureData(byteInputStream, XWPFDocument.PICTURE_TYPE_PNG);
//将图片插入word
//String ind = document.addPictureData(conn.getInputStream(),XWPFDocument.PICTURE_TYPE_PNG);
//替换图片
document.getPackagePart().addRelationship(document.getPackagePart().getRelationship(ind).getTargetURI(), TargetMode.INTERNAL, XWPFRelation.IMAGES.getRelation(), imagePosition);
} catch (Exception e) {
log.info("报错了");
e.printStackTrace();
}
//导出到文件
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
document.write((OutputStream) byteArrayOutputStream);

String fileName = "new"+contractno;
File file = new File(outPath + fileName + ".docx");
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
FileOutputStream outputStream = new FileOutputStream(outPath + fileName + ".docx");
outputStream.write(byteArrayOutputStream.toByteArray());
outputStream.flush();
outputStream.close();
FileUtil.deleteFile(outPath+contractno+".docx");
}

word文档转pdf

jar包引入
org.apache.poi.xwpf.converter.core包中引用了poi相关的jar包,根据实际情况修改相关依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>org.apache.poi.xwpf.converter.core</artifactId>
<version>1.0.6</version>
<exclusions>
<exclusion>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
</exclusion>
<exclusion>
<artifactId>xmlbeans</artifactId>
<groupId>org.apache.xmlbeans</groupId>
</exclusion>
</exclusions>

</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>org.apache.poi.xwpf.converter.pdf</artifactId>
<version>1.0.6</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.itext.extension</artifactId>
<version>1.0.6</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.10</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.13</version>
</dependency>

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public String makePdfByXcode(String fileName){
try {
XWPFDocument document=new XWPFDocument(new FileInputStream(new File(outPath+fileName+".docx")));
File outFile=new File(outPath+fileName+".pdf");
outFile.getParentFile().mkdirs();
OutputStream out=new FileOutputStream(outFile);
PdfOptions options= PdfOptions.create(); //gb2312
PdfConverter.getInstance().convert(document,out,options);
FileUtil.deleteFile(outPath+fileName+".docx");
return pdf;
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}

出现的问题

无法读取文档中字符
打开document.xml检查需替换的字符格式,一个单词可能存在多种格式,重新编写文档,确保格式一致
存在多张图片时图片错位
正常图片:
正常图片
错位图片:
错位图片
图片右键>其他布局选项:
布局:
布局
linux中文空白
原因:linux缺少中文字符
C:\Windows\Fonts内的中文字库复制到服务器/usr/share/fonts
安装字体索引指令
yum install mkfontscale
建立字体索引信息,更新字体缓存
mkfontscale && mkfontdir && fc-cache -fv
查看是否按照成功
fc-list :lang=zh
重新启动服务,加载字体
win10字体