背景
之前一直是用户点击下载word文件到本地,然后使用office或者wps打开。需求优化,要实现可以直接在线预览,无需下载到本地然后再打开。
随后开始上网找资料,网上资料一大堆,方案也各有不同,大概有这么几种方案:
1.word转html然后转pdf
2.Openoffice + swftools + Flexmapper + jodconverter
3.kkFileView
分析之后最后决定使用Openoffice+PDF.js方式实现
环境搭建
1.安装Openoffice,下载地址:http://www.openoffice.org/download/index.html
安装完成之后,cmd进入安装目录执行命令:soffice "-accept=socket,host=localhost,port=8100;urp;StarOffice.ServiceManager" -nologo -headless -nofirststartwizard
2.PDF.js,下载地址:http://mozilla.github.io/pdf.js/
下载之后解压,目录结构如下:
代码实现
编码方面,分前端后:
后端:java后端使用openoffice把word文档转换成pdf文件,返回流
前端:把PDF.js解压后的文件加到项目中,修改对应路径,PDF.js拿到后端返回的流直接展示
后端
项目使用springboot,pom文件添加依赖
com.artofsolving
jodconverter
2.2.1
org.openoffice
jurt
3.0.1
org.openoffice
ridl
3.0.1
org.openoffice
juh
3.0.1
org.openoffice
unoil
3.0.1
application.properties配置openoffice服务地址与端口openoffice.host=127.0.0.1
openoffice.port=8100
doc文件转pdf文件
import java.io.BufferedInputStream;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.OutputStream;import java.net.ConnectException;import javax.servlet.http.HttpServletResponse;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import com.xxx.utils.Doc2PdfUtil;
@Controller
@RequestMapping("/doc2PdfController")public class Doc2PdfController {
@Value("${openoffice.host}") private String OpenOfficeHost;
@Value("${openoffice.port}") private Integer OpenOfficePort;
private Logger logger = LoggerFactory.getLogger(Doc2PdfController.class);
@RequestMapping("/doc2pdf") public void doc2pdf(String fileName,HttpServletResponse response){
File pdfFile = null;
OutputStream outputStream = null;
BufferedInputStream bufferedInputStream = null;
Doc2PdfUtil doc2PdfUtil = new Doc2PdfUtil(OpenOfficeHost, OpenOfficePort);
try { //doc转pdf,返回pdf文件
pdfFile = doc2PdfUtil.doc2Pdf(fileName);
outputStream = response.getOutputStream();
response.setContentType("application/pdf;charset=UTF-8");
bufferedInputStream = new BufferedInputStream(new FileInputStream(pdfFile));
byte buffBytes[] = new byte[1024];
outputStream = response.getOutputStream();
int read = 0;
while ((read = bufferedInputStream.read(buffBytes)) != -1) {
outputStream.write(buffBytes, 0, read);
}
} catch (ConnectException e) {
logger.info("****调用Doc2PdfUtil doc转pdf失败****");
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally { if(outputStream != null){ try {
outputStream.flush();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
} if(bufferedInputStream != null){ try {
bufferedInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
import java.io.File;import java.net.ConnectException;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import com.artofsolving.jodconverter.DocumentConverter;import com.artofsolving.jodconverter.openoffice.connection.OpenOfficeConnection;import com.artofsolving.jodconverter.openoffice.connection.SocketOpenOfficeConnection;import com.artofsolving.jodconverter.openoffice.converter.StreamOpenOfficeDocumentConverter;public class Doc2PdfUtil { private String OpenOfficeHost; //openOffice服务地址
private Integer OpenOfficePort; //openOffice服务端口
public Doc2PdfUtil(){
} public Doc2PdfUtil(String OpenOfficeHost, Integer OpenOfficePort){ this.OpenOfficeHost = OpenOfficeHost; this.OpenOfficePort = OpenOfficePort;
}
private Logger logger = LoggerFactory.getLogger(Doc2PdfUtil.class);
/**
* doc转pdf
* @return pdf文件路径
* @throws ConnectException */
public File doc2Pdf(String fileName) throws ConnectException{
File docFile = new File(fileName + ".doc");
File pdfFile = new File(fileName + ".pdf"); if (docFile.exists()) { if (!pdfFile.exists()) {
OpenOfficeConnection connection = new SocketOpenOfficeConnection(OpenOfficeHost, OpenOfficePort); try {
connection.connect();
DocumentConverter converter = new StreamOpenOfficeDocumentConverter(connection); //最核心的操作,doc转pdf converter.convert(docFile, pdfFile);
connection.disconnect();
logger.info("****pdf转换成功,PDF输出:" + pdfFile.getPath() + "****");
} catch (java.net.ConnectException e) {
logger.info("****pdf转换异常,openoffice服务未启动!****");
e.printStackTrace(); throw e;
} catch (com.artofsolving.jodconverter.openoffice.connection.OpenOfficeException e) {
System.out.println("****pdf转换器异常,读取转换文件失败****");
e.printStackTrace(); throw e;
} catch (Exception e) {
e.printStackTrace(); throw e;
}
}
} else {
logger.info("****pdf转换异常,需要转换的doc文档不存在,无法转换****");
} return pdfFile;
}
}
前端
把pdfjs-2.0.943-dist下的两个文件夹build、web整体加到项目中,然后把viewer.html改成viewer.jsp,并调整了位置,去掉了默认的pdf文件compressed.tracemonkey-pldi-09.pdf,将来使用我们生成的文件
viewer.jsp、viewer.js注意点:
1.引用的js、css路径要修改过来
2.viewer.jsp中调用pdf/web/viewer.js,viewer.js中配置了默认的pdf文件路径,我们要动态生成pdf,因此需要修改,在jsp中定义一个参数DEFAULT_URL,然后在js中使用它
3.jsp中写了一个ajax获取pdf流,之后赋值给DEFAULT_URL,然后再让viewer.js去加载,因此需要把/pdf/web/viewer.js放到ajax方法后面
4.viewer.js中把compressed.tracemonkey-pldi-09.pdf改成我们定义的变量DEFAULT_URL;pdf.worker.js的路径修改成对应路径
pageEncoding="utf-8"%>html>
var qtpath = '${qtpath}'; var fileName = '${fileName}';
PDF.js viewer
var DEFAULT_URL = "";//注意,删除的变量在这里重新定义
var PDFData = "";
$.ajax({
type:"post",
async:false, // mimeType: 'text/plain; charset=x-user-defined',
url:'${qtpath}/doc2PdfController/doc2pdf',
data:{'fileName':fileName},
success:function(data){
PDFData = data;
}
});
var rawLength = PDFData.length;
//转换成pdf.js能直接解析的Uint8Array类型,见pdf.js-4068
var array = new Uint8Array(new ArrayBuffer(rawLength));
for(i = 0; i
array[i] = PDFData.charCodeAt(i) & 0xff;
}
DEFAULT_URL = array;
...
效果
分割线
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
本以为完美的实现了doc在线预览,上测试环境后发现了一个大坑,我们的doc文件不是在本地office创建后上传的,是其他同事用freemarker ftl模板生成的,这种生成的doc文件根本不是微软标准的doc,本质是xml数据结构,openoffice拿这种文件去转换pdf文件直接就报错了
上网查资料查了半天也没找到这种问题的解决方案,想想只能是放弃openoffice改用其他方法了(freemarker ftl生成doc这个肯定是不能动的)
看到一些博客使用word--html--pdf生成pdf,还有的使用freemarker ftl xml 生成pdf感觉还是太繁琐了,我只是想拿现有的doc(虽然是freemarker ftl生成的)转换成pdf啊
继续看博客查资料,看到一种方法,使用aspose把doc转换成pdf,抱着试一试的心态在本地测试了下,没想到竟然成了,感觉太意外了,aspose方法超级简单,只要导入jar包,几行代码就可以搞定,并且转换速度比openoffice要快很多。很是奇怪,这么好用这么简单的工具为什么没在我一开始搜索word转pdf的时候就出现呢
aspose doc转pdf
在maven仓库搜索aspose,然后把依赖加入pom.xml发现jar包下载不下来,没办法,最后在下载aspose jar包,然后mvn deploy到仓库
pom.xml
com.aspose.words
aspose-words-jdk16
14.9.0
import java.io.BufferedInputStream;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.OutputStream;import java.net.ConnectException;import javax.servlet.http.HttpServletResponse;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import com.xxx.utils.Doc2PdfUtil;
@Controller
@RequestMapping("/doc2PdfController")public class Doc2PdfController {
private Logger logger = LoggerFactory.getLogger(Doc2PdfController.class);
@RequestMapping("/doc2pdf") public void doc2pdf(String fileName,HttpServletResponse response){
File pdfFile = null;
OutputStream outputStream = null;
BufferedInputStream bufferedInputStream = null;
String docPath = fileName + ".doc";
String pdfPath = fileName + ".pdf"; try {
pdfFile = Doc2PdfUtil.doc2Pdf(docPath, pdfPath);
outputStream = response.getOutputStream();
response.setContentType("application/pdf;charset=UTF-8");
bufferedInputStream = new BufferedInputStream(new FileInputStream(pdfFile));
byte buffBytes[] = new byte[1024];
outputStream = response.getOutputStream();
int read = 0;
while ((read = bufferedInputStream.read(buffBytes)) != -1) {
outputStream.write(buffBytes, 0, read);
}
} catch (ConnectException e) {
logger.info("****调用Doc2PdfUtil doc转pdf失败****");
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally { if(outputStream != null){ try {
outputStream.flush();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
} if(bufferedInputStream != null){ try {
bufferedInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
Doc2PdfUtil.java
import java.io.ByteArrayInputStream;import java.io.File;import java.io.FileOutputStream;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import com.aspose.words.License;import com.aspose.words.SaveFormat;public class Doc2PdfUtil {
private static Logger logger = LoggerFactory.getLogger(Doc2PdfUtil.class);
/**
* doc转pdf
* @param docPath doc文件路径,包含.doc
* @param pdfPath pdf文件路径,包含.pdf
* @return
*/
public static File doc2Pdf(String docPath, String pdfPath){
File pdfFile = new File(pdfPath); try {
String s = "Aspose.Total for JavaAspose.Words for JavaEnterprise20991231209912318bfe198c-7f0c-4ef8-8ff0-acc3237bf0d7sNLLKGMUdF0r8O1kKilWAGdgfs2BvJb/2Xp8p5iuDVfZXmhppo+d0Ran1P9TKdjV4ABwAgKXxJ3jcQTqE/2IRfqwnPf8itN8aFZlV3TJPYeD3yWE7IT55Gz6EijUpC7aKeoohTb4w2fpox58wWoF3SNp6sK6jDfiAUGEHYJ9pjU=";
ByteArrayInputStream is = new ByteArrayInputStream(s.getBytes());
License license = new License();
license.setLicense(is);
com.aspose.words.Document document = new com.aspose.words.Document(docPath);
document.save(new FileOutputStream(pdfFile),SaveFormat.PDF);
} catch (Exception e) {
logger.info("****aspose doc转pdf异常");
e.printStackTrace();
} return pdfFile;
}
}
aspose-words-jdk16-14.9.0.jar下载地址