问题
开发过程,需要将批量的office文档,包括word文档(doc,docx),ppt文档(ppt,pptx),excel文档(xls,xlsx)等转成pdf,以此来实现预览。市面上常用的方案包括有openoffice,libreoffice等。
openoffice
其中openoffice是一个软件,需要在服务器上安装该软件,然后通过命令的形式调用openoffice转换组件(我是在java里头使用),来实现转换。虽然可行,但是问题也是存在的。
- 使用久了,容易在后台运行多个openoffice的服务,内存占用比较大
- openoffice转换excel文档,会出现换行的情况,如下:
原来excel内容格式:
经过openoffice转换之后:
libreoffice
跟openoffice很像,也是一个软件,使用方式,也是先安装软件,然后通过命令的形式来调用转换组件(我是基于node来调用的),因为出现了跟openoffice一样的问题:转换excel会换行,所以我直接不考虑了。
解决
经过多方的查探,终于发现了可以通过使用aspose来转换(此处如果涉及到aspose侵权的问题,请联系笔者下架该文章,笔者也是使用aspose学习用),我使用aspose也做了多次的使用方式升级。
第一次,直接通过java来调用api,实现转换,速度挺快的,2-3s就可以转换一个
第二次,将转换的api打包成可执行jar包,供node程序来调用(因为node本身程序比较轻便,而转换的话,靠java的aspose的api,因此使用这种方式来完成)
第一种方式,我这边就不演示了,直接上第二种方式的源码:
- 利用idea创建一个最简单的maven工程
- 先上下后面全部做好的项目的目录:
- 配置pom文件
- 配置内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>convertPdf</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>com.future.aspose</groupId>
<artifactId>aspose.slides-15.9.0</artifactId>
<version>15.9.0</version>
</dependency>
<dependency>
<groupId>com.future.aspose</groupId>
<artifactId>aspose.words-16.8.0</artifactId>
<version>16.8.0</version>
</dependency>
<dependency>
<groupId>com.future.aspose</groupId>
<artifactId>aspose.pdf-11.8.0</artifactId>
<version>11.8.0</version>
</dependency>
<dependency>
<groupId>com.future.aspose</groupId>
<artifactId>aspose.cells-9.0.0</artifactId>
<version>9.0.0</version>
</dependency>
</dependencies>
<build>
<finalName>convertPdf</finalName>
<plugins>
<plugin>
<!-- 配置插件坐标 -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.6</version>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<!-- 此处指定main方法入口的class -->
<mainClass>org.example.ConvertPdf</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
- 创建文件夹和工具类文件
- 创建文件ConverPdf文件(查看代码的话,直接从最后一行的main方法开始看,方便理解)
package org.example;
import com.aspose.cells.*;
import com.aspose.slides.Presentation;
import com.aspose.slides.SaveFormat;
import com.aspose.words.Document;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Objects;
/**
* @author 扫地僧
*/
public class ConvertPdf {
/**
* 获取license
* @return
*/
public static void getWordLicense() throws Exception {
try (InputStream license = Thread.currentThread().getContextClassLoader().getResourceAsStream("./license.xml")) {
com.aspose.words.License aposeLic = new com.aspose.words.License();
aposeLic.setLicense(license);
} catch (Exception e){
throw new Exception("验证License失败!");
}
}
/**
* 获取license
* @return
*/
public static void getPPTLicense() throws Exception {
try (InputStream license = Thread.currentThread().getContextClassLoader().getResourceAsStream("./license.xml")){
com.aspose.slides.License aposeLic = new com.aspose.slides.License();
aposeLic.setLicense(license);
} catch (Exception e){
throw new Exception("验证License失败!");
}
}
/**
* 获取license
* @return
*/
public static void getExcelLicense() throws Exception {
try (InputStream license = Thread.currentThread().getContextClassLoader().getResourceAsStream("./license.xml")){
License aposeLic = new License();
aposeLic.setLicense(license);
} catch (Exception e){
throw new Exception("验证License失败!");
}
}
public static void word2Pdf(InputStream in, String pdfinputFile) throws Exception{
// 验证License
getWordLicense();
try (FileOutputStream fileOS = new FileOutputStream(new File(pdfinputFile))) {
Document doc = new Document(in);
doc.save(fileOS, com.aspose.words.SaveFormat.PDF);
}
}
public static void ppt2Pdf(InputStream in, String pdfinputFile) throws Exception{
// 验证License
getPPTLicense();
try (FileOutputStream fileOS = new FileOutputStream(new File(pdfinputFile))) {
Presentation ppt = new Presentation(in);
ppt.save(fileOS, SaveFormat.Pdf);
}
}
public static void excel2Pdf(InputStream in, String pdfinputFile) throws Exception {
// 验证License
getExcelLicense();
Workbook excel = new Workbook(in);
PdfSaveOptions pdfOptions = new PdfSaveOptions();
pdfOptions.setOnePagePerSheet(true);
Style style = excel.createStyle();
style.setBorder(BorderType.BOTTOM_BORDER, CellBorderType.THIN, Color.getLightGray());
style.setBorder(BorderType.LEFT_BORDER, CellBorderType.THIN, Color.getLightGray());
style.setBorder(BorderType.TOP_BORDER, CellBorderType.THIN, Color.getLightGray());
style.setBorder(BorderType.RIGHT_BORDER, CellBorderType.THIN, Color.getLightGray());
excel.setDefaultStyle(style);
excel.save(pdfinputFile,pdfOptions);
}
/**
* 执行转换
*/
public static int execute (String inputFile, String outputFile) {
System.out.println("[文件转pdf]开始...");
System.out.println("[文件转pdf]目标文件:" + inputFile);
try {
File originalFile = new File(inputFile);
if (!originalFile.exists()) {
throw new Exception("文件不存在");
}
try (FileInputStream in = new FileInputStream(inputFile)) {
if (inputFile.endsWith(".xlsx") || inputFile.endsWith(".xls")) {
outputFile += ".pdf";
FileUtils.deleteFile(outputFile);
excel2Pdf(in,outputFile);
} else if (inputFile.endsWith(".pptx") || inputFile.endsWith(".ppt")){
// 先删除再转换
outputFile += ".pdf";
FileUtils.deleteFile(outputFile);
ppt2Pdf(in, outputFile);
} else if (inputFile.endsWith(".doc") || inputFile.endsWith(".docx") ) {
// 先删除再转换
outputFile += ".pdf";
FileUtils.deleteFile(outputFile);
word2Pdf(in,outputFile);
} else {
String postfix = "." + FileUtils.getPostfix(inputFile);
// 先删除再转换
outputFile += postfix;
FileUtils.deleteFile(outputFile);
FileUtils.copyFile(inputFile, FileUtils.getPrefix(outputFile), outputFile);
}
}
} catch (Exception e) {
System.out.println("[文件转pdf]异常:" + e.getMessage());
}
System.out.println("[文件转pdf]结束!");
return fileExist(outputFile);
}
/**
* 判断文件是否存在 存在代表转换成功
* @return
*/
private static int fileExist (String inputFile) {
if (Objects.nonNull(inputFile) && (new File(inputFile)).exists()) {
return 1;
}
return 0;
}
/**
* 因为是要打包成可执行的jar包,因此输入的参数要从args里头获取
* 打包成功控制台输出1
* 打包失败控制台输出0
* 通过捕捉控制台的信息,就可以知道是否打包成功了
* @param args
*/
public static void main(String[] args) {
if (args.length < 2) {
System.out.println(0);
} else {
String inputFile = args[0];
String outputFile = args[1];
if (Objects.isNull(inputFile) || Objects.isNull(outputFile)) {
System.out.println(0);
} else {
// 调用转换pdf的API
System.out.println(execute(inputFile, outputFile));
}
}
}
}
- 创建FileUtils文件
package org.example;
import java.io.*;
/**
* org.example.FileUtils.java
* description:文件处理工具类
* author 魏霖涛
* @version $Revision: 1.3 $ $Date: 2017/02/06 07:02:48 $ $Author: weilintao $
* history 1.0.0 2016-1-6 created by weilintao
*/
public class FileUtils {
/**
* 获取输出文件
*
* @param inputFilePath
* @return
*/
public static String getOutputFilePath(String inputFilePath) {
String outputFilePath = inputFilePath.replaceAll("." + getPostfix(inputFilePath), ".pdf");
return outputFilePath;
}
/**
* 获取inputFilePath的后缀名,如:"e:/test.pptx"的后缀名为:"pptx"<br>
*
* @param inputFilePath
* @return
*/
public static String getPostfix(String inputFilePath) {
return inputFilePath.substring(inputFilePath.lastIndexOf(".") + 1);
}
/**
* 获取inputFilePath的前缀,如:"e:/test.pptx"的前缀为:"e:/"<br>
*
* @param inputFilePath
* @return
*/
public static String getPrefix(String inputFilePath) {
return inputFilePath.substring(0, inputFilePath.lastIndexOf("/") + 1);
}
/**
* 获取inputFilePath的文件名,如:"e:/test.pptx"的文件名为:"test.pptx"<br>
* modify: 添加如果lastIndexOf("/") == -1的判断
*
* @param inputFilePath
* @return
*/
public static String getFileName(String inputFilePath) {
if (inputFilePath.lastIndexOf("/") == -1) {
return inputFilePath;
}
return inputFilePath.substring(inputFilePath.lastIndexOf("/")+1);
}
/**
* 删除文件
* @param filepath
*/
public static void deleteFile(String filepath){
File file = new File(filepath);
if(file.exists()){
file.delete();
}
}
/**
* 删除文件夹
*
* @param folderPath 文件夹完整绝对路径
*
*/
public static void delFolder(String folderPath) {
try {
// 删除完里面所有内容
delAllFile(folderPath);
String filePath = folderPath;
filePath = filePath.toString();
File myFilePath = new File(filePath);
// 删除空文件夹
myFilePath.delete();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 删除指定文件夹下所有文件
*
* @param path 文件夹完整绝对路径
*
*
*/
public static boolean delAllFile(String path) {
boolean bea = false;
File file = new File(path);
if (!file.exists()) {
return bea;
}
if (!file.isDirectory()) {
return bea;
}
String[] tempList = file.list();
File temp = null;
for (int i = 0; i < tempList.length; i++) {
if (path.endsWith(File.separator)) {
temp = new File(path + tempList[i]);
} else {
temp = new File(path + File.separator + tempList[i]);
}
if (temp.isFile()) {
temp.delete();
}
if (temp.isDirectory()) {
// 先删除文件夹里面的文件
delAllFile(path + "/" + tempList[i]);
// 再删除空文件夹
delFolder(path + "/" + tempList[i]);
bea = true;
}
}
return bea;
}
public static void deleteFile(File file){
if(file.exists()){
file.delete();
}
}
/**
* 新建目录
* @param directory 目录路径 如: e:/
*/
public static void createFileDirectory(String directory){
File file = new File(directory);
if(!file.exists()){
//几级目录没有就建立几级 mkdir:只能建立第一级
file.mkdirs();
}
}
/**
* 复制单个文件
* @param oldPath String 原文件路径 如:c:/fqf.txt
* @param newPath String 复制后路径 如:f:/
* @param filename String 目标文件名 如:fqf.txt
* boolean
*/
public static void copyFile(String oldPath, String newPath, String filename) {
InputStream inStream = null;
FileOutputStream fs = null;
try {
int bytesum = 0;
int byteread = 0;
File oldfile = new File(oldPath);
//文件存在时
if (oldfile.exists()) {
//读入原文件
inStream = new FileInputStream(oldPath);
createFileDirectory(newPath);
fs = new FileOutputStream(newPath + filename);
byte[] buffer = new byte[1444];
while ( (byteread = inStream.read(buffer)) != -1) {
//字节数 文件大小
bytesum += byteread;
fs.write(buffer, 0, byteread);
}
System.out.println("该文件总共字节数为:" + bytesum);
inStream.close();
} else {
System.out.println("文件"+oldPath+"不存在,退出");
}
}catch (Exception e) {
System.out.println("复制单个文件操作出错");
e.printStackTrace();
}finally{
if(inStream != null){
try {
inStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fs != null){
try {
fs.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static boolean fileExist(String filepath){
File file = new File(filepath);
if(file.exists()){
return true;
}
return false;
}
public static boolean directoryExist(String filepath){
File file = new File(filepath);
if(file.exists()){
return true;
}
return false;
}
public static void main(String args[]){
}
/**
* 复制文件 复制文件夹时候用
* @param sourcefile
* @param targetFile
* @throws IOException
*/
public static void copyFile(File sourcefile,File targetFile){
try{
//新建文件输入流并对它进行缓冲
FileInputStream input=new FileInputStream(sourcefile);
BufferedInputStream inbuff=new BufferedInputStream(input);
//新建文件输出流并对它进行缓冲
FileOutputStream out=new FileOutputStream(targetFile);
BufferedOutputStream outbuff=new BufferedOutputStream(out);
//缓冲数组
byte[] b=new byte[1024*5];
int len=0;
while((len=inbuff.read(b))!=-1){
outbuff.write(b, 0, len);
}
//刷新此缓冲的输出流
outbuff.flush();
//关闭流
inbuff.close();
outbuff.close();
out.close();
input.close();
}catch (Exception e) {
e.printStackTrace();
}
}
/**
* 复制文件夹里面的内容到另外一个文件夹
* @param sourceDir 源文件夹 D:/min_res/video/1565"
* @param targetDir 目标文件夹 D:/min_res/video/15651" 这个文件夹允许不存在
* @throws IOException
*/
public static void copyDirectiory(String sourceDir,String targetDir){
try{
//新建目标目录
(new File(targetDir)).mkdirs();
//获取源文件夹当下的文件或目录
File[] file=(new File(sourceDir)).listFiles();
for (int i = 0; i < file.length; i++) {
if(file[i].isFile()){
//源文件
File sourceFile=file[i];
//目标文件
File targetFile=new File(new File(targetDir).getAbsolutePath()+File.separator+file[i].getName());
copyFile(sourceFile, targetFile);
}
if(file[i].isDirectory()){
//准备复制的源文件夹
String dir1=sourceDir+"/"+file[i].getName();
//准备复制的目标文件夹
String dir2=targetDir+"/"+file[i].getName();
copyDirectiory(dir1, dir2);
}
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
- 创建license.xml文件
<License>
<Data>
<Products>
<Product>Aspose.Total for Java</Product>
<Product>Aspose.Words for Java</Product>
</Products>
<EditionType>Enterprise</EditionType>
<SubscriptionExpiry>20991231</SubscriptionExpiry>
<LicenseExpiry>20991231</LicenseExpiry>
<SerialNumber>8bfe198c-7f0c-4ef8-8ff0-acc3237bf0d7</SerialNumber>
</Data>
<Signature>sNLLKGMUdF0r8O1kKilWAGdgfs2BvJb/2Xp8p5iuDVfZXmhppo+d0Ran1P9TKdjV4ABwAgKXxJ3jcQTqE/2IRfqwnPf8itN8aFZlV3TJPYeD3yWE7IT55Gz6EijUpC7aKeoohTb4w2fpox58wWoF3SNp6sK6jDfiAUGEHYJ9pjU=</Signature>
</License>
- 上述工作做好之后,项目就都准备好了,然后就是执行maven的package命令,打包生成一个jar包文件(这个步骤要是不会的,可以评论区留言,因为这个步骤比较简单,因此这边不上教程了)
- 打包生成的文件如下:
- 上述的convertPdf就是我们要的可执行jar包,该jar使用的方式是,在cmd窗口通过java -jar convertPdf.jar 待转换office文件全路径 转换成功的pdf文件的路径命令来使用
- eg: java -jar ./convertPdf.jar e:/input/111.doc e:/output/111 (注意:输出文件111表示文件的名称(后缀名不要添加))
- 编写node程序来调用jar文件:
let child_process = require('child_process');
let iconv = require('iconv-lite');
let encoding = 'cp936';
let binaryEncoding = 'binary';
/**
* 转换office文档和pdf文档为pdf文档
*
* @param inputFile 输入文件全路径 eg: e:/input/111.doc
* @param outputFile 输出文件路径和文件名,eg: e:/output/111 111表示文件的名称(后缀名不要添加)
* @return {Promise<any>}
*/
let convert = (inputFile, outputFile)=>{
return new Promise(((resolve, reject) => {
let cmdStr = `java -jar ./convertPdf.jar ${inputFile} ${outputFile}`;
child_process.exec(cmdStr,{ encoding: binaryEncoding },function(err,stdout,srderr){
if (err) {
reject(iconv.decode(new Buffer(srderr, binaryEncoding), encoding))
} else {
resolve(iconv.decode(new Buffer(stdout, binaryEncoding), encoding))
}
});
})).catch(e=>{
console.error(`[office文档转换]转换失败:${e.message}`)
})
}
/**
* 格式化cmd命令执行的结果
* @param msg
* @return {*}
*/
let formatMsg = (msg)=>{
let tmp = msg.split('\r\n')
if (tmp.length > 0) {
tmp.splice(tmp.length - 1, 1)
return tmp
} else {
return null
}
}
// 测试代码
(async ()=>{
let result = await convert("C:\\Users\\zp89\\Desktop\\pdf转换测试\\ce.xlsx", "C:\\Users\\zp89\\Desktop\\pdf转换测试\\" + Date.now())
console.log("运行结果:")
console.log(formatMsg(result))
})()
- 效果如下(测试的excel有两个sheet)