问题
开发过程,需要将批量的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>
-
- 创建文件ConverPdf文件(查看代码的话,直接从最后一行的main方法开始看,方便理解)
- 上述工作做好之后,项目就都准备好了,然后就是执行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)