根据word模板生成word、转换成pdf、打成war包

一、word模板

 

 

 

二、替换模板里的内容(创建一个WordUtils工具类)

import org.apache.poi.xwpf.usermodel.*;

import java.io.*;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class WordUtils {
/**
* 替换表格里面的变量
* @param doc 要替换的文档
* @param params 参数
*/
public static void replaceInTable(XWPFDocument doc, Map<String, Object> params) {
Iterator<XWPFTable> iterator = doc.getTablesIterator();
XWPFTable table;
List<XWPFTableRow> rows;
List<XWPFTableCell> cells;
List<XWPFParagraph> paragraphs = doc.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
List<XWPFRun> runs = paragraph.getRuns();
for (XWPFRun run : runs) {
run.setText(changeValue(run.toString(), params),0);
}
}
while (iterator.hasNext()) {
table = iterator.next();
rows = table.getRows();
      // 删除行
      //table.removeRow(3);
            for (XWPFTableRow row : rows) {
cells = row.getTableCells();
for (XWPFTableCell cell : cells) {
List<XWPFParagraph> paras = cell.getParagraphs();
for (XWPFParagraph paragraph : paras) {
List<XWPFRun> runs = paragraph.getRuns();
for (XWPFRun run : runs) {
run.setText(changeValue(run.toString(), params),0);
}
}
}
}
    mergeCellsVertically(table, 0, 1,2);
    mergeCellsVertically(table, 3, 1,2);
        }
}

/**
* 匹配传入信息集合与模板
* @param value 模板需要替换的区域
* @param textMap 传入信息集合
* @return 模板需要替换区域信息集合对应值
*/
private static String changeValue(String value, Map<String, Object> textMap){
Set<Map.Entry<String, Object>> textSets = textMap.entrySet();
for (Map.Entry<String, Object> textSet : textSets) {
//匹配模板与替换值 格式${key}
String key = "${"+textSet.getKey()+"}";
if(value.indexOf(key)!= -1){
value = textSet.getValue().toString();
}
}
return value;
}

/**
* 关闭输入流
* @param is
*/
public static void close(InputStream is) {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

/**
* 关闭输出流
* @param os
*/
public static void close(OutputStream os) {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}


}

三、实现类   reportService

/**   convertPath路径D:/template/convert   

Map<String, Object> params       例:params.put("name", "张三");   // 与模板里设置的变量一致

**/

private ResponseEntity<byte[]> templateWrite(String filePath, Map<String, Object> params, int n) throws IOException {

         String convertPath = path + File.separator + "template" + File.separator + "convert";

    File dir = new File(convertPath);
if (!dir.exists()) {
dir.mkdir();
}
String docxFilePath = convertPath + File.separator + "tb" + n + ".docx";
String pdfFilePath = convertPath + File.separator + "pdf" + File.separator + "tb" + n + ".pdf";
InputStream is = new FileInputStream(filePath);
XWPFDocument doc = new XWPFDocument(is);
// 替换表格里面的变量
WordUtils.replaceInTable(doc, params);

OutputStream os = new FileOutputStream(docxFilePath);
doc.write(os);
WordUtils.close(os);
WordUtils.close(is);
os.flush();
os.close();

return this.convertToPdf(docxFilePath, pdfFilePath);
}
private ResponseEntity<byte[]> convertToPdf(String docxFilePath, String pdfFilePath) throws IOException {
// word转pdf
String targetPath = path + File.separator + "template" + File.separator + "convert" + File.separator + "pdf";
LibreofficeUtils.wordConverterToPdf(docxFilePath, targetPath, osName);

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_PDF);
headers.setContentDispositionFormData("attachment", "tb.pdf");
  //输入docx
   //headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
//headers.setContentDispositionFormData("attachment", "tb.docx");

ResponseEntity<byte[]> returnFile = new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(new File(pdfFilePath)), headers, HttpStatus.OK);

deleteFile(new File(path + File.separator + "template" + File.separator + "convert" ));

return returnFile;
}

 四、controller类

@RequestMapping(value = "/download-pdf", method = RequestMethod.GET)
public ResponseEntity<byte[]> downloadPDF(HttpServletResponse response, ExamReportDTO examReport) throws Exception {
return reportService.exportPDF(response, examReport);
}

 五、pom.xml    (第一次word转Pdf用的是poi,4.1.2版本太高,所以转换报错,需要降低版本)

<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<!--<version>4.1.2</version>-->
<version>3.12</version>
</dependency>

<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<!--<version>4.1.2</version>-->
<version>3.12</version>
</dependency>

<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<!-- <version>4.1.2</version>-->
<version>3.12</version>
</dependency>

<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<!--<version>4.1.2</version>-->
<version>3.12</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.apache.xmlbeans/xmlbeans -->
<dependency>
<groupId>org.apache.xmlbeans</groupId>
<artifactId>xmlbeans</artifactId>
<version>3.1.0</version>
</dependency>

<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>org.apache.poi.xwpf.converter.pdf</artifactId>
<version>1.0.6</version>
</dependency>

<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.11</version>
</dependency>

<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>

<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>org.odftoolkit.odfdom.converter.pdf</artifactId>
<version>1.0.6</version>
</dependency>

<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>

 六、最终实现此需求经历的过程

1、先使用的itext,根据pdf模板,填写区域值,直接生成pdf文件,无需转换。

难点:需要安装Adobe Acrobat DC,按照如下步骤,设计表单的域值。

Pdf模板准备好后,可以自行百度,查询itext如何根据pdf模板,填充内容

结果:最终没有选择这个方式,主要是因为,生成的pdf时,域值框,背景色是蓝色,没有多余时间研究如何变成白色,所以放弃了。

 

 

 

 

 

 2、使用poi   实现word转换pdf 

service类     

InputStream docxInputStream = new FileInputStream(docxFilePath);
XWPFDocument document = new XWPFDocument(docxInputStream);
OutputStream out = new FileOutputStream(pdfFilePath);

PdfOptions options = PdfOptions.create();
document.createNumbering();
PdfConverter.getInstance().convert(document, out, options); // 需要注意,此类只能用低版本的Poi,所以把原来的4.1.2版本降为了3.12
docxInputStream.close();
out.close();

结果:此方式有不足,word有复选框或者和合并单元格这种复杂样式时,转换后的Pdf格式错乱,所以放弃此方法

 

3、使用libreoffice

需要安装liberoffice ,并配置path环境变量   

 

安装完后,可以从过cmd,来测试是否可行

 

 pdf 包路径     .docx文件路径   

 

 1)创建LibreofficeUtils工具类 

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;

public class LibreofficeUtils {

public static boolean wordConverterToPdf(String docxPath,String targetPath, String osName) throws IOException {
File file = new File(docxPath);
try {
String command = "";
if (osName.contains("Windows")) {
command = "soffice --convert-to pdf -outdir " + targetPath + " " + docxPath;
} else {
command = "doc2pdf --output=" + targetPath + File.separator + file.getName().replaceAll(".(?i)docx", ".pdf") + " " + docxPath;
}
String result = executeCommand(command);
if (result.equals("") || result.contains("writer_pdf_Export")) {
return true;
}
} catch (Exception e) {
e.printStackTrace();
throw e;
}
return false;
}

private static String executeCommand(String command) {
StringBuffer output = new StringBuffer();
Process p;
InputStreamReader inputStreamReader = null;
BufferedReader reader = null;
try {
p = Runtime.getRuntime().exec(command);
p.waitFor();
inputStreamReader = new InputStreamReader(p.getInputStream(), "UTF-8");
reader = new BufferedReader(inputStreamReader);
String line = "";
while ((line = reader.readLine()) != null) {
output.append(line + "\n");
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
try {
if (reader != null) {
reader.close();
}
if (inputStreamReader != null) {
inputStreamReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println(output.toString());
return output.toString();
}
}

2) service 类

 // word转pdf
String targetPath = path + File.separator + "template" + File.separator + "convert" + File.separator + "pdf";
LibreofficeUtils.wordConverterToPdf(docxFilePath, targetPath, osName);

3)用到的jar包
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
结果:经过多种方法尝试,此方法可行


七、补充下,把pdf打成zip包代码实现

service实现类:
    /**
* 多文件打成zip包
*
* @param path 如:D:
*/
private void pdfToZip(String path) {
// String convertPath = path + File.separator + "template" + File.separator + "convert" + File.separator + "pdf";
String convertPath = path + File.separator + "template" + File.separator + "convert";
File dir = new File(convertPath);
if (dir.isDirectory()) {
List<File> file = new ArrayList<>();
File[] files = dir.listFiles();
File zipFile = new File(convertPath + File.separator + "tianbao.zip");
for (int i = 0; i < files.length; i++) {
File f = files[i];
String fileName = f.getName();
String suffix = fileName.substring(fileName.lastIndexOf("."));
// if (suffix.equals(".pdf")) {
if (suffix.equals(".docx")) {
file.add(f);
}
}
ZipUtils.toZip(file, zipFile);
}
}

// 打包完zip后,输出
String convertPath = path + File.separator + "template" + File.separator + "convert";
// String zipPath = convertPath + File.separator + "pdf" + File.separator;
String zipPath = convertPath + File.separator;
HttpHeaders headers = new HttpHeaders();
headers.setContentDispositionFormData("attachment", "tb.zip");
ResponseEntity<byte[]> returnFile = new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(new File(zipPath + "tb.zip")), headers, HttpStatus.OK);
        // 删除文件
deleteFile(new File(convertPath));
      retrun returnFile;


private void deleteFile(File file) {
if (file.isDirectory()) {
File[] files = file.listFiles();
for (int i = 0; i < files.length; i++) {
deleteFile(files[i]);
}
}
file.delete();
}


import org.springframework.boot.container.core.log.ZKLogger;
import org.springframework.boot.container.core.log.ZKLoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class ZipUtils {
private static final ZKLogger logger = ZKLoggerFactory.getLogger(ZipUtils.class);

/**
* 把文件集合打成zip压缩包
* @param srcFiles 压缩文件集合
* @param zipFile zip文件名
* @throws RuntimeException 异常
*/
public static void toZip(List<File> srcFiles, File zipFile) throws RuntimeException {
long start = System.currentTimeMillis();
if(zipFile == null){
logger.error("压缩包文件名为空!");
return;
}
if(!zipFile.getName().endsWith(".zip")){
logger.error("压缩包文件名异常,zipFile={}", zipFile.getPath());
return;
}
ZipOutputStream zos = null;
FileOutputStream out = null;
try {
out = new FileOutputStream(zipFile);
zos = new ZipOutputStream(out);
for (File srcFile : srcFiles) {
byte[] buf = new byte[10*1024];
zos.putNextEntry(new ZipEntry(srcFile.getName()));
int len;
FileInputStream in = new FileInputStream(srcFile);
while ((len = in.read(buf)) != -1) {
zos.write(buf, 0, len);
}
//zos.setComment("我是注释");
in.close();
}
zos.closeEntry();
long end = System.currentTimeMillis();
logger.info("压缩完成,耗时:" + (end - start) + " ms");
} catch (Exception e) {
logger.error("ZipUtil toZip exception, ", e);
throw new RuntimeException("zipFile error from ZipUtils", e);
} finally {
if (zos != null) {
try {
zos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}


由于需求变更,WordUtils里添加合并单元格功能:
/**
* word跨行并单元格
* @param table 表格
* @param col 合并行所在列
* @param fromRow 开始行
* @param toRow 结束行
*/
public static void mergeCellsVertically(XWPFTable table, int col, int fromRow, int toRow) {
for (int rowIndex = fromRow; rowIndex <= toRow; rowIndex++) {
XWPFTableCell cell = table.getRow(rowIndex).getCell(col);
if ( rowIndex == fromRow ) {
cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
} else {
cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
}
}
}








posted on 2020-11-23 17:57  ssk&lzs  阅读(589)  评论(0编辑  收藏  举报

导航