springboot实战之office文档在线预览
- 2019 年 11 月 27 日
- 笔记
文档在线预览在企业级应用开发也算是比较常遇见的需求了,通常处理这方面的需求大致有如下的方案,
1、购买成熟的第三方产品
比如永中DCS,其产品介绍可以查看如下链接
http://dcs.yozosoft.com/help.html
在比如idocv,其产品介绍可以查看如下链接
https://www.idocv.com/docs.html
2、自研实现文档预览服务器
标题取得高大上,常用的方法基本上也是基于第三方类库进行实现
- 比如flash的flexpaper将文档转换为swf格式,然后使用flash在网页中浏览
- 在比如java可以使用jodConverter+openoffice/libreoffice
3、文档预览是选择成熟第三产品还是自研?
如果公司成本预算充足,建议使用第三方成熟的产品,俗话说术业有专攻,正常专门做这方面的产品,不管是在技术上还是运维上都会比较成熟可靠,如果成本有限,还是自研吧,实现一个简单版本的文档预览服务器还是比较容易,但就是可能会采坑比较多。这边的选择只是针对文档预览,而非所有技术、产品选择都按这样选择,毕竟很多时候的自研的成本远远大于购买第三方服务,要具体情况具体分析
下边以jodconverter+LibreOffice为例,来实现一个简单的文档预览。这边为啥选LibreOffice而不选OpenOffice,其原因可以参考如下如下文章
OpenOffice与LibreOffice,哪个更适合你 https://www.linuxdashen.com/openoffice%E4%B8%8Elibreoffice%EF%BC%8C%E5%93%AA%E4%B8%AA%E6%9B%B4%E9%80%82%E5%90%88%E4%BD%A0 Apache OpenOffice 与 LibreOffice 之间的抉择 https://yq.aliyun.com/articles/81336
技术产品选型,可以从产品官方文档入手,产品的更新迭代活跃度、社区活跃度、是否易用等维度来进行考量,总归一句话不管黑猫白猫,能抓住老鼠的猫就是好猫,能够满足需求就行
正文
实现流程

安装LibreOffice
1、window环境
可以通过下方链接下载,并按提示一步一步安装
https://zh-cn.libreoffice.org/download/download/
2、centos环境
#安装文件 yum -y install libreoffice #安装中文包 yum -y install libreoffice-langpack-zh-Han* #安装的目录在/usr/lib64/libreoffice
通过命令行测试LibreOffice
#centos /usr/bin/libreoffice --invisible --convert-to pdf <待转换的word路径> --outdir <生成的pdf路径> 比如:/usr/bin/libreoffice --invisible --convert-to pdf test.txt --outdir abc # windows soffice.exe --headless --invisible --convert-to pdf test.txt --outdir d:abc
代码实现
1、pom.xml
<!-- office预览相关--> <dependency> <groupId>org.jodconverter</groupId> <artifactId>jodconverter-core</artifactId> </dependency> <dependency> <groupId>org.jodconverter</groupId> <artifactId>jodconverter-local</artifactId> </dependency> <dependency> <groupId>org.jodconverter</groupId> <artifactId>jodconverter-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.jodconverter</groupId> <artifactId>jodconverter-online</artifactId> </dependency> <dependency> <groupId>org.libreoffice</groupId> <artifactId>ridl</artifactId> </dependency>
2、application.yml
jodconverter: local: enabled: true office-home: ${office.home} port-numbers: 8100,8101,8102 max-tasks-per-process: 100 store: path: ${store.path}
其中office.home和store.path,这两个属性一个是LibreOffice的安装路径,一个是LibreOffice转换后存放文档的路径,其中这两个属性的具体内容配置在pom.xml,内容如下
<profiles> <profile> <id>win</id> <properties> <office.home>D:/Program Files/LibreOffice</office.home> <store.path>D:/data/preview</store.path> </properties> <activation> <activeByDefault>true</activeByDefault> </activation> </profile> <profile> <id>linux</id> <properties> <office.home>/usr/lib64/libreoffice</office.home> <store.path>/data/preview</store.path> </properties> <activation> <activeByDefault>false</activeByDefault> </activation> </profile> </profiles>
通过maven的profile来指定环境,默认是window环境,如果要切换到linux环境,则打包是执行
mvn clean package -P linux
或者打包时,改下pom.xml,把默认环境切换为linux
3、转换文档的核心代码
@Service @Slf4j public class PreviewServiceImpl implements PreviewService { @Value("${jodconverter.store.path}") private String storePath; @Autowired private DocumentConverter documentConverter; @Override public FileConvertResultDTO convertFile2pdf(File sourceFile,String fileExt) { FileConvertResultDTO fileConvertResultDTO = new FileConvertResultDTO(); try { fileExt = fileExt.toLowerCase(); String fileName = FileUtil.getWithoutExtension(sourceFile.getName()); String targetFileExt = getTargetFileExt(fileExt); File targetFile = new File(storePath+ FileUtil.SLASH_ONE + fileName + FileUtil.DOT + targetFileExt); documentConverter.convert(sourceFile).as(DefaultDocumentFormatRegistry.getFormatByExtension(fileExt)) .to(targetFile).as(DefaultDocumentFormatRegistry.getFormatByExtension(targetFileExt)).execute(); fileConvertResultDTO.setStatus("success"); fileConvertResultDTO.setTargetFileName(targetFile.getName()); } catch (OfficeException e) { log.error("convertFile2pdf error : " + e.getMessage(),e); fileConvertResultDTO.setStatus("fail"); } return fileConvertResultDTO; } @Override public FileConvertResultDTO convertInputStream2pdf(InputStream in, String fileName, String fileExt) { FileConvertResultDTO fileConvertResultDTO = new FileConvertResultDTO(); try { fileExt = fileExt.toLowerCase(); fileName = FileUtil.getWithoutExtension(fileName); String targetFileExt = getTargetFileExt(fileExt); File targetFile = new File(storePath+ FileUtil.SLASH_ONE + fileName + FileUtil.DOT + targetFileExt); documentConverter.convert(in).as(DefaultDocumentFormatRegistry.getFormatByExtension(fileExt)) .to(targetFile).as(DefaultDocumentFormatRegistry.getFormatByExtension(targetFileExt)).execute(); fileConvertResultDTO.setStatus("success"); fileConvertResultDTO.setTargetFileName(targetFile.getName()); } catch (OfficeException e) { log.error("convertInputStream2pdf error : " + e.getMessage(),e); fileConvertResultDTO.setStatus("fail"); } return fileConvertResultDTO; } /** * 获取想要转换的格式类型 * @return */ private String getTargetFileExt(String originFileExt){ if(Constants.fileType2Htmls.contains(originFileExt)){ return FileUtil.HTML; } return FileUtil.PDF; } @PostConstruct private void init(){ File targetDir = new File(storePath); if(!targetDir.exists()){ targetDir.mkdirs(); } } }
4、拉取office服务器文档代码
@GetMapping(value="/readFile") public ResponseEntity<byte[]> readFile(String fileName){ if(StringUtils.isBlank(fileName)){ log.warn("fileName is blank"); return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } String fileExt = FileUtil.getExtension(fileName); if(StringUtils.isBlank(fileExt)){ fileName = fileName + FileUtil.PDF; } String filePath = storePath + FileUtil.SLASH_ONE + fileName; File file = new File(filePath); if(!file.exists()){ log.warn("fileName:{} is not found",fileName); return new ResponseEntity<>(HttpStatus.NOT_FOUND); } try { //判断文件类型 String mimeType = URLConnection.guessContentTypeFromName(file.getName()); if(mimeType == null) { mimeType = "application/octet-stream"; } response.setContentType(mimeType); //设置文件响应大小 response.setContentLengthLong(file.length()); byte[] bytes = FileUtil.readFileToByteArray(file); response.getOutputStream().write(bytes); return new ResponseEntity<>(bytes,HttpStatus.OK); } catch (IOException e) { log.error("readFile error:"+e.getMessage(),e); } return new ResponseEntity<>(HttpStatus.EXPECTATION_FAILED); }
5、测试验证
测试步骤按上方的流程图来测试 a、上传文档


b、点击确定按钮,进行跳转预览

参考文档
文档在线预览方案
https://blog.csdn.net/xiaqingxue930914/article/details/81121581
SpringBoot使用LibreOffice转换PDF
https://segmentfault.com/a/1190000015129654
总结
java版的office文档预览,本质上就利用jodconverter去连接openoffice或libreoffice服务,相当于我们自己实现的office服务器本质上算是一个openoffice或libreoffice代理服务器。在实现过程中,当excel转换pdf时,会存在一些坑点,比如excel的列的宽度大的时候生成的pdf会自动换行,有多个sheet页的时候默认也只能生成出来一个。解决的方案是,当遇到文档类型为excel时,就不要转换为pdf格式,而是转换为html格式
demo链接
https://github.com/lyb-geek/springboot-learning/tree/master/springboot-office-preview