ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# 一、介绍 前端框架的文件预览功能,实现思路是后端UPMS服务将office文档先转换成pdf文件,前端页面展示PDF文件,后端采用openoffice转换office文档。 目前已知的可以通过openoffice将文档转换为PDF或者HTML以及CSV。其他的转换类型需要自行摸索。 ***** # 二、openoffice安装 [安装包下载地址](http://www.openoffice.org/download/index.html) ~~~ 1、下载安装包,部署UPMS服务器的任一目录,命令下载: wget https://jaist.dl.sourceforge.net/project/openofficeorg.mirror/4.1.7/binaries/zh-CN/Apache_OpenOffice_4.1.7_Linux_x86-64_install-rpm_zh-CN.tar.gz 2、解压:tar -zxvf Apache_OpenOffice_4.1.7_Linux_x86-64_install-rpm_zh-CN.tar.gz 3、进入cd zh-CN/RPMS,安装必要的包,执行 yum localinstall *.rpm 4、再进入RPMS/desktop-integration目录,执行:rpm -ivh openoffice4.1.7-redhat-menus-4.1.7-9790.noarch.rpm 5、启动Openoffice服务: cd /opt/openoffice4/program soffice -headless -accept="socket,host=127.0.0.1,port=8100;urp;" -nofirststartwizard & 完成本地启动,如果需要远程访问,需要将host地址改成机器对应IP地址 6、查看服务是否启动(端口8100是否被soffice占用):netstat -lnp |grep 8100 显示结果:tcp 0 0 127.0.0.1:8100 0.0.0.0:* LISTEN 19501/soffice.bin 7、因为一般安装完后openoffice在转换pdf时候会因为缺失字体造成中文乱码情况,所以一般要将window下的字体库打包拷贝到linux服务器上。 (1) 查看系统字体文件cat /etc/fonts/fonts.conf (2) 将window下的字符库上传到linux下。一般是在/usr/share/fonts目录下,创建fonts目录。 (3) 执行fc-cache。 (4) 重启openoffice。 ~~~ ***** # 三、中文字体安装 如果需要预览的文档中包含中文字体,则需要在安装了openoffice软件的linux服务器上安装中文字体,安装过程如下: ~~~ 1、使用fc-list查看服务器系统中已经安装的字体;如果没有该命令则可以根据实际情况使用如下命令安装: 在centos上,使用如下命令进行安装: yum install -y fontconfig mkfontscale 在ubuntu上,使用如下命令进行安装: sudo apt-get -y install fontconfig xfonts-utils 如果要查看系统中已经安装的中文字体,我们可以使用如下命令: fc-list :lang=zh 2、上传Windows系统中的中文字体安装文件到服务器上的/usr/share/fonts/目录: 进入C:\Windows\Fonts目录,该目录下的所有的字体安装包都是可以上传到服务器的 选择一个或几个常用的中文字体安装包上传到服务器的/usr/share/fonts/目录 然后建立字体索引信息,更新字体缓存,使用如下命令: cd /usr/share/fonts/ mkfontscale mkfontdir fc-cache 3、重启openoffice 使用kill命令杀死openoffice进程 执行上一章节的第5步骤 ~~~ ***** # 四、项目中使用openoffice将文档转换为PDF预览 使用快开平台框架在项目中进行文档转换及预览时,需要文件先上传到minio文件服务中心,然后才能进行后续的转换及预览 ## 4.1、修改upms配置 1. 进入nacos注册中心 2. 进入“配置管理/配置中心”菜单页面 3. 编辑sca-upms-biz的服务配置 4. 修改配置内容,示例说明如下: ``` #openoffice配置 jodconverter: local: enabled: false #是否启用openoffice max-tasks-per-process: 10 #设置一个office进程在重启之前所能执行的最大任务数。默认为200个 port-numbers: 8100 #openoffice端口号 office-home: /opt/openoffice4/ #该属性是office的安装目录 . ``` 更详细的配置说明可以前往该网址查看[jobconverter文档详解](https://blog.csdn.net/qingtian_1993/article/details/79901843) 5. 发布最新配置 6. 重启upms服务 ***** ## 4.2、bootstrap页面使用预览功能示例 ![](https://img.kancloud.cn/a0/59/a05947f8e10588726ef7482460f5244e_2160x1057.jpg) ``` function viewFile(filename,bucketName) { var urlView = api + '/admin/sys-file/fileView/' + bucketName + '/' + filename var windowUrl = window.URL || window.webkitURL;//处理浏览器兼容性 var xhr = new XMLHttpRequest(); xhr.open("GET", urlView, true); xhr.responseType = "blob"; // xhr.setRequestHeader("Authorization", 'Bearer ' + session.get('token'),); xhr.onload = function () { if (this.status == 200) { var blob = this.response; var url=""; if (window.createObjectURL != undefined) { // basic url = window.createObjectURL(blob); } else if (window.webkitURL != undefined) { // webkit or chrome try { url = window.webkitURL.createObjectURL(blob); } catch (error) {} } else if (window.URL != undefined) { // mozilla(firefox) try { url = window.URL.createObjectURL(blob); } catch (error) {} } window.open('/static/js/pdf/web/viewer.html?file='+encodeURIComponent(url)) } } xhr.send(); } ``` 主要是访问`/admin/sys-file/fileView/ + bucketName + '/' + filename`后台请求地址,其中需要传递的那个参数分别是minio中的桶名称bucketName和需要预览的文件名称filename。 更完整的示例可以参看bootstrap前端框架下的`static\pages\sysadm\file`目录下的fileList.js文件 ***** ## 4.3、vue页面使用预览功能示例 ![页面示例](https://img.kancloud.cn/a0/6d/a06d76cf136e51fd4ef424466ca171e0_2149x680.jpg) ``` <script> import { handleDown,previewPdf } from '@/util/util' export default { name: 'sys-file', components:{uploadCard}, data() { return { } }, created() { }, mounted: function() { }, computed: { ...mapGetters(['permissions']), }, methods: { viewPdf(row, index){ let type = matchType(row.fileName) if (type === 'word' || type === 'excel' || type === 'ppt' || type === 'pdf' || type === 'txt'){ previewPdf(row.bucketName,row.fileName) }else if (type === 'image' || type === 'video' || type === 'radio'){ this.$message.error('该文件暂不支持预览') }else{ this.$message.error('该文件暂不支持预览') } }, } } </script> ``` 调用`previewPdf`方法即可在浏览器中打开新的标签页进行文件预览,可以参考vue前端框架下的`src\views\admin\file`下的index.vue文件。 ***** ## 4.4、在后台代码中集成openoffice ### 首先添加依赖 ``` <dependency> <groupId>org.jodconverter</groupId> <artifactId>jodconverter-spring-boot-starter</artifactId> <version>4.3.0</version> </dependency> <dependency> <groupId>org.jodconverter</groupId> <artifactId>jodconverter-core</artifactId> <version>4.3.0</version> </dependency> <dependency> <groupId>org.jodconverter</groupId> <artifactId>jodconverter-local</artifactId> <version>4.3.0</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-io/commons-io --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.8.0</version> </dependency> <dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.3.1</version> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.3.2</version> </dependency> <dependency> <groupId>com.github.livesense</groupId> <artifactId>jodconverter-core</artifactId> <version>1.0.5</version> </dependency> ``` ### 添加配置 配置说明参考本文档`4.1、修改upms配置`章节 ### 注入类 ``` @Autowired(required = false) private DocumentConverter converter; ``` ### 使用示例 ``` /** * 文件转PDF预览 * * @param bucket * 桶名称 * @param fileName * 文件空间/名称 * @param response * @return */ @Inner(false) @GetMapping("/fileView/{bucket}/{fileName}") public void fileView(@PathVariable String bucket, @PathVariable String fileName, HttpServletResponse response) { //获取文件并转换为input流 InputStream inputStream = sysFileService.getFile(bucket, fileName); File targetFile = this.transfer(fileName, inputStream); try { if (targetFile != null) { response.setContentType("application/pdf; charset=UTF-8"); IoUtil.copy(cn.hutool.core.io.IoUtil.toStream(targetFile), response.getOutputStream()); } } catch (Exception e) { log.error("文件转换异常: {}", e.getLocalizedMessage()); } finally { cn.hutool.core.io.IoUtil.close(inputStream); if (targetFile != null) { //为了不占用服务器资源,故获取PDF文件流后,最后将临时文件删除 targetFile.deleteOnExit(); } } } /* * 将文件转换为PDF文档,根据需求可转换为其他类型 */ private File transfer(String fileName, InputStream inputStream) { File targetFile = null; try { targetFile = java.io.File.createTempFile(fileName.substring(0, fileName.lastIndexOf(".")), ".pdf"); // 文件转化 converter.convert(inputStream).to(targetFile).execute(); } catch (Exception e) { log.error("文件读取异常: {}", e.getLocalizedMessage()); } return targetFile; } ``` 网上可参考的集成示例:[SpringBoot2.x整合OpenOffice4](https://blog.csdn.net/li_jiazhi/article/details/105386337)、[SpringBoot集成jodconverter使用openoffice将word转为pdf](https://www.jianshu.com/p/77e0b6f5c7dd) ### 设置自定义转换PDF宽度 当excel宽度超过A4纸宽度后,需要实现如下代码,将PDF的宽度设置长,就可以避免excel转换为PDF时被默认截取 ``` import org.jodconverter.core.DocumentConverter; import org.jodconverter.core.document.DocumentFormatRegistry; import org.jodconverter.core.office.OfficeManager; import org.jodconverter.local.LocalConverter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @Author xiangyu * @Date 2021/3/19 17:13 * @Version 1.0 */ @Configuration public class JodConverterConfiguration { @Bean DocumentConverter localDocumentConverter(OfficeManager localOfficeManager, DocumentFormatRegistry documentFormatRegistry) { return LocalConverter.builder().filterChain( // new PageMarginsFilter(0,0,0,0), // 对word有用,对excel似乎没什么用 new JodConverterRefreshFilter(true) ).officeManager(localOfficeManager).formatRegistry(documentFormatRegistry).build(); } } ``` ``` import com.sun.star.awt.Size; import com.sun.star.beans.PropertyValue; import com.sun.star.lang.IllegalArgumentException; import com.sun.star.lang.XComponent; import com.sun.star.uno.UnoRuntime; import com.sun.star.view.PaperFormat; import com.sun.star.view.XPrintable; import org.jodconverter.core.office.OfficeContext; import org.jodconverter.local.filter.FilterChain; import org.jodconverter.local.filter.RefreshFilter; /** * @Author xiangyu * @Date 2021/3/19 17:16 * @Version 1.0 */ public class JodConverterRefreshFilter extends RefreshFilter { public final static Size A6, A5, A4, A3, A2, A1, A0; public final static Size B6, B5, B4, B3, B2, B1, B0; static { A6 = new Size(10500, 14800); A5 = new Size(14800, 21000); A4 = new Size(21000, 29700); A3 = new Size(29700, 42000); A2 = new Size(42000, 59400); A1 = new Size(59400, 84100); A0 = new Size(84100, 118900); B6 = new Size(12500, 17600); B5 = new Size(17600, 25000); B4 = new Size(25000, 35300); B3 = new Size(35300, 50000); B2 = new Size(50000, 70700); B1 = new Size(70700, 100000); B0 = new Size(100000, 141400); } /** * Creates a new refresh filter. */ public JodConverterRefreshFilter() { this(false); } /** * Creates a new refresh filter that will call or not the next filter in the chain according to * the specified argument. * * @param lastFilter If {@code true}, then the filter won't call the next filter in the chain. If * {@code false}, the next filter in the chain, if any, will be applied. */ public JodConverterRefreshFilter(final boolean lastFilter) { super(lastFilter); } @Override public void doFilter( final OfficeContext context, final XComponent document, final FilterChain chain) throws Exception { setPaperInfo(document, new Size(67000, 20000)); super.doFilter(context, document, chain); } /** * 设置纸张信息 * 请注意!!!PaperOrientation 和 PaperSize 不可以同时配置,否则有一个无效! * <p> * The default paper format and orientation is A4 and portrait. * * @param paperSize */ private void setPaperInfo(final XComponent document, final Size paperSize) throws IllegalArgumentException { XPrintable xPrintable = UnoRuntime.queryInterface(XPrintable.class, document); PropertyValue[] printerDesc = new PropertyValue[2]; // Paper Orientation(纵向横向) // printerDesc[0] = new PropertyValue(); // printerDesc[0].Name = "PaperOrientation"; // printerDesc[0].Value = PaperOrientation.LANDSCAPE; // Paper Format printerDesc[0] = new PropertyValue(); printerDesc[0].Name = "PaperFormat"; printerDesc[0].Value = PaperFormat.USER; // Paper Size printerDesc[1] = new PropertyValue(); printerDesc[1].Name = "PaperSize"; printerDesc[1].Value = paperSize; xPrintable.setPrinter(printerDesc); } } ```