方案调研:
方法1.poi读取+ itext(不能使用)生成pdf (效果最差,跨平台)
方法2.jodconverter + openOffice (一般格式实现效果还行,复杂格式容易有错位,跨平台,其中:JODConverter可以作为Java库,嵌到java应用程序中;openOffice需要下载并安装到服务器,启动openOffice的服务)
方法3.jacob + msOfficeWord + SaveAsPDFandXPS (完美保持原doc格式,效率最慢,只能在windows环境下进行,需要安装msofficeWord以及SaveAsPDFandXPS.exe(word的一个插件,用来把word转化为pdf))
方法4.Aspose实现,需要许可证 Aspose网站:https://products.aspose.com/
本文采用方法2
jodconverter和pdfbox的依赖,项目是springboot架构,所以引入以下依赖
<dependency>
<groupId>org.apache.pdfboxgroupId>
<artifactId>pdfboxartifactId>
<version>2.0.8version>
dependency>
<dependency>
<groupId>com.artofsolvinggroupId>
<artifactId>jodconverterartifactId>
<version>2.2.1version>
dependency>
<dependency>
<groupId>org.jodconvertergroupId>
<artifactId>jodconverter-spring-boot-starterartifactId>
<version>4.3.0version>
dependency>
<dependency>
<groupId>org.jodconvertergroupId>
<artifactId>jodconverter-coreartifactId>
<version>4.3.0version>
dependency>
package com.cigna.hmc.groupinsurance.utils.excel;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import org.apache.commons.lang.StringUtils;
import com.artofsolving.jodconverter.DefaultDocumentFormatRegistry;
import com.artofsolving.jodconverter.DocumentConverter;
import com.artofsolving.jodconverter.DocumentFormatRegistry;
import com.artofsolving.jodconverter.openoffice.connection.OpenOfficeConnection;
import com.artofsolving.jodconverter.openoffice.connection.SocketOpenOfficeConnection;
import com.artofsolving.jodconverter.openoffice.converter.OpenOfficeDocumentConverter;
import com.artofsolving.jodconverter.openoffice.converter.StreamOpenOfficeDocumentConverter;
/**
*
* @author josnow
* @date 2017年5月9日 下午12:38:39
* @version 1.0.0
* @desc openoffice转换工具
*/
public class OpenOfficeUtils {
public static final String LOCAL_HOST = "localhost";
public static final int LOCAL_PORT = 8100;
// Format
public static DocumentFormatRegistry formatFactory = new DefaultDocumentFormatRegistry();
/**
*
* @desc
* @auth josnow
* @date 2017年6月9日 下午4:11:04
* @param inputFilePath
* 待转换的文件路径
* @param outputFilePath
* 输出文件路径
*/
public static void convert(String inputFilePath, String outputFilePath) throws ConnectException {
convert(inputFilePath, outputFilePath, LOCAL_HOST, LOCAL_PORT);
}
/**
*
* @desc
* @auth josnow
* @date 2017年6月9日 下午4:12:29
* @param inputFilePath
* 待转换的文件路径
* @param outputFilePath
* 输出文件路径
* @param connectIp
* 远程调用ip
* @param connectPort
* 远程调用端口
*/
public static void convert(String inputFilePath, String outputFilePath, String connectIp, int connectPort)
throws ConnectException {
if (StringUtils.isEmpty(inputFilePath) || StringUtils.isEmpty(outputFilePath)
|| StringUtils.isEmpty(connectIp)) {
throw new IllegalArgumentException("参数异常!!");
}
OpenOfficeConnection connection = new SocketOpenOfficeConnection(connectIp, connectPort);
connection.connect();
DocumentConverter converter = getConverter(connectIp, connection);
converter.convert(new File(inputFilePath), new File(outputFilePath));
connection.disconnect();
}
/**
*
* @desc
* @auth josnow
* @date 2017年6月9日 下午4:08:26
* @param inputStream
* @param inputFileExtension
* 待转换文件的扩展名,例如: xls,doc
* @param outputStream
* @param outputFileExtension
* 输出文件扩展名,例如:pdf
*/
public static void convert(InputStream inputStream, String inputFileExtension, OutputStream outputStream,
String outputFileExtension) throws ConnectException {
convert(inputStream, inputFileExtension, outputStream, outputFileExtension, LOCAL_HOST, LOCAL_PORT);
}
/**
*
* @desc
* @auth josnow
* @date 2017年6月9日 下午4:10:21
* @param inputStream
* @param inputFileExtension
* 待转换文件的扩展名,例如: xls,doc
* @param outputStream
* @param outputFileExtension
* 输出文件扩展名,例如:pdf
* @param connectIp
* 远程调用ip
* @param connectPort
* 远程调用端口
*/
public static void convert(InputStream inputStream, String inputFileExtension, OutputStream outputStream,
String outputFileExtension, String connectIp, int connectPort) throws ConnectException {
if (inputStream == null || StringUtils.isEmpty(inputFileExtension) || outputStream == null
|| StringUtils.isEmpty(outputFileExtension) || StringUtils.isEmpty(connectIp)) {
throw new IllegalArgumentException("参数异常!!");
}
OpenOfficeConnection connection = new SocketOpenOfficeConnection(connectIp, connectPort);
connection.connect();
DocumentConverter converter = getConverter(connectIp, connection);
converter.convert(inputStream, formatFactory.getFormatByFileExtension(inputFileExtension), outputStream,
formatFactory.getFormatByFileExtension(outputFileExtension));
connection.disconnect();
}
private static DocumentConverter getConverter(String connectIp, OpenOfficeConnection connection) {
DocumentConverter converter = "localhost".equals(connectIp) || "127.0.0.1".equals(connectIp)
|| "0:0:0:0:0:0:0:1".equals(connectIp) ? new OpenOfficeDocumentConverter(connection)
: new StreamOpenOfficeDocumentConverter(connection);
return converter;
}
}
public static final String PNG = "png";
/**
* 通过PDFbox生成文件的缩略图
*
* @param filePath:文件路径
* @param outPath:输出图片路径
* @throws IOException
*/
public static void getPic(String filePath, String outPath) throws IOException {
PDDocument pdDocument = null;
ImageOutputStream imageOut = null;
try {
// 利用PdfBox生成图像
pdDocument = PDDocument.load(new File(filePath));
PDFRenderer renderer = new PDFRenderer(pdDocument);
// 构造图片 dpi 每英寸点数
BufferedImage imgTemp = renderer.renderImageWithDPI(0, 60, ImageType.RGB);
// 设置图片格式
Iterator<ImageWriter> it = ImageIO.getImageWritersBySuffix(PNG);
// 将文件写出
ImageWriter writer = it.next();
imageOut = ImageIO.createImageOutputStream(new FileOutputStream(outPath));
writer.setOutput(imageOut);
writer.write(new IIOImage(imgTemp, null, null));
imgTemp.flush();
imageOut.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
imageOut.close();
pdDocument.close();
}
}
/**
* 通过PDFbox生成文件的预览图
*
* @param filePath:文件路径
* @param outPath:输出图片路径
* @throws IOException
*/
public static List<String> getPreviews(String filePath, String outPath) throws IOException {
PDDocument pdDocument = null;
ImageOutputStream imageOut = null;
List<String> imageOutPathList = new ArrayList<>();
try {
// 利用PdfBox生成图像
pdDocument = PDDocument.load(new File(filePath));
// This will return the total page count of the PDF document.
int numberOfPages = pdDocument.getNumberOfPages();
numberOfPages = numberOfPages > 20 ? 20 : numberOfPages;
PDFRenderer renderer = new PDFRenderer(pdDocument);
StringBuffer stringBuffer = new StringBuffer(outPath);
for (int i = 0; i < numberOfPages; i++) {
imageOutPathList.add(stringBuffer.append(i).append(".").append(PNG).toString());
// 构造图片 dpi 每英寸点数
BufferedImage imgTemp = renderer.renderImageWithDPI(i, 60, ImageType.RGB);
// 设置图片格式
Iterator<ImageWriter> it = ImageIO.getImageWritersBySuffix(PNG);
// 将文件写出
ImageWriter writer = it.next();
imageOut = ImageIO.createImageOutputStream(new FileOutputStream(stringBuffer.toString()));
writer.setOutput(imageOut);
writer.write(new IIOImage(imgTemp, null, null));
imgTemp.flush();
imageOut.flush();
stringBuffer = new StringBuffer(outPath);
}
} catch (IOException e) {
log.error("getPreviews error" + e.getMessage());
}finally {
pdDocument.close();
try{
if(imageOut!=null){
imageOut.close();
}
}catch (Exception ex){
log.error("OpenOfficeUtil getThumbnails imageOut close ex. {}",ex.getMessage(),ex);
}
return imageOutPathList;
}
}
类
OpenOfficeDocumentConverter
默认的基于文件的DocumentConverter实现。
此实现将文档数据作为文件 URL 传入和传出 OpenOffice.org 服务。
基于文件的转换比基于流的转换更快(由 提供 StreamOpenOfficeDocumentConverter),但它们需要 OpenOffice.org 服务在本地运行并且对文件具有正确的权限
。
类
StreamOpenOfficeDocumentConverter
替代的基于流的DocumentConverter
实现。
此实现将文档数据作为流传入和传出 OpenOffice.org 服务。
基于流的转换比默认的基于文件的转换慢(由 提供[
OpenOfficeDocumentConverter](http://jodconverter.sourceforge.net/api/com/artofsolving/jodconverter/openoffice/converter/OpenOfficeDocumentConverter.html)),但它们允许在不同的机器上运行 OpenOffice.org 服务,或者在同一台机器上的不同系统用户下运行而不会出现文件权限问题
。
Java使用openoffice将office系列文档转换为PDF
搭建好 OpenOffice + jodconverter 后,转换doc(97-2003)时正常,但是转换 docx 时报了以下错误:
java.lang.IllegalArgumentException: unknown document format for file: E:\word.docx
openoffice2.2.2是支持转换的,但Maven 中没有 2.2.2版本的。
openoffice2.2.1依赖jar,以maven为例:
<dependency>
<groupId>com.artofsolvinggroupId>
<artifactId>jodconverterartifactId>
<version>2.2.1version>
dependency>
jodconverter 在转换2007版本以后的xxx.docx文档会报错,原因大家都明03后缀名xxx.doc 07以后版本xxx.docx
解决方案
重写
BasicDocumentFormatRegistry类中public DocumentFormat getFormatByFileExtension(String extension)方法,只要是后缀名包含doc则使用doc的documentFormat文档格式
重写类BasicDocumentFormatRegistry的目的是为了覆盖jar包里的BasicDocumentFormatRegistry类
,要求是必须报名和类名完全相同(原理是会先加载当前工程里的class文件,再去加载jar包里的class文件,当已有BasicDocumentFormatRegistry时,不会再去加载jar包里的类)
当自己重写的BasicDocumentFormatRegistry也会被打成jar包被依赖时,因为都在jar包里,所以不会起作用。
其实此时应该考虑写个新的类来继承DefaultDocumentFormatRegistry
,自己实现getFormatByFileExtension方法,通过调用时的多态,达到调用时用子类实现的目的
。
在项目的java下com目录下新建artofsolving.jodconverter这两个包
package com.artofsolving.jodconverter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @ClassName: online
* @description: 重写 BasicDocumentFormatRegistry 文档格式
* @Author: yandongfa
* @Data: 2020-03-24 19:47
* @Version: 1.0
**/
@Primary
@Component
public class BasicDocumentFormatRegistry implements DocumentFormatRegistry {
private List/* */ documentFormats = new ArrayList();
public void addDocumentFormat(DocumentFormat documentFormat) {
documentFormats.add(documentFormat);
}
protected List/* */ getDocumentFormats() {
return documentFormats;
}
/**
* @param extension
* the file extension
* @return the DocumentFormat for this extension, or null if the extension
* is not mapped
*/
public DocumentFormat getFormatByFileExtension(String extension) {
if (extension == null) {
return null;
}
extension = extension.toLowerCase();
//new DefaultDocumentFormatRegistry();
//将文件名后缀统一转化
if (extension.indexOf("doc") >= 0) {
extension = "doc";
}
if (extension.indexOf("ppt") >= 0) {
extension = "ppt";
}
if (extension.indexOf("xls") >= 0) {
extension = "xls";
}
for (Iterator it = documentFormats.iterator(); it.hasNext();) {
DocumentFormat format = (DocumentFormat) it.next();
if (format.getFileExtension().equals(extension)) {
return format;
}
}
return null;
}
public DocumentFormat getFormatByMimeType(String mimeType) {
for (Iterator it = documentFormats.iterator(); it.hasNext();) {
DocumentFormat format = (DocumentFormat) it.next();
if (format.getMimeType().equals(mimeType)) {
return format;
}
}
return null;
}
}
原文链接:https://blog.csdn.net/qq_41107231/article/details/105880512
openOffice下载:https://www.openoffice.org/download/
下载,安装完成后
以服务方式启动OpenOffice
将OpenOffice加入用户环境变量
vim ~/.zshrc //有的可能是.bashrc文件
在.zshrc文件中增加 export PATH=$PATH:/Applications/OpenOffice.app/Contents/MacOS
一句。
source ~/.zshrc //使环境变量立即生效
使用以下命令启动OpenOffice
注意:本地连接
openoffice使用host=localhost
或host=127.0.0.1 ;远程连接
使用host=0.0.0.0
soffice -headless -accept="socket,host=localhost,port=8100;urp;" -nofirststartwizard
此命令将已服务的方式启动OpenOffice,并监听本机的8100端口
注意要守护模式启动容器(-d 镜像名:tag)
docker run -u 123456 --name openoffice -p 8100:8100 -v /data/:/data/ -d docker.io/xiaojun207/openoffice4-daemon:latest
参考文档:原文