1024刚过,也祝愿各位码友在从此生活中,身体健康,事事顺心,再无Bug。php
以前写过一篇文章关于上传目录文件: uni-app系统目录文件上传(非只图片和视频)解决方案,此次来解决文件预览问题。
uni-app 是一个使用 Vue.js 开发全部前端应用的框架,开发者编写一套代码,可发布到iOS、Android、H五、以及各类小程序(微信/支付宝/百度/头条/QQ/钉钉)等多个平台。在作业务系统时,不可避免会遇到文件在线预览的需求。这里的文件包括PDF、Word、Excel、PPT、图片等。而在线预览不是下载后再用本地软件或浏览器打开预览,而是直接经过文件流的形式查看。本方案主要解决在线预览问题,以及在uni-app开发过程当中遇到一系列问题。html
若是有欠缺的地方,或者有更好的方案,还望各位码友多提意见,多多交流,文章最后能够加我。前端
文件预览,首先会想到pdf预览,前端作pdf预览,首先也会想到 pdf.js,那咱们就从 pdf.js提及。
pdf.js开源地址和在线例子
Github
Online Demo
<img src="https://user-gold-cdn.xitu.io...;h=288&f=png&s=32729" width="240" align=center />vue
<img src="https://user-gold-cdn.xitu.io...;h=262&f=png&s=17092" width="240" align=center />java
新建vue组件file-preview.vueios
<template> <view> <web-view :src="allUrl"></web-view> </view> </template> <script> import globalConfig from '@/config' export default { data() { return { viewerUrl: '/hybrid/html/web/viewer.html', // viewerUrl: globalConfig.baseUrl + '/pdf/web/viewer.html', allUrl: '' } }, onLoad(options) { let fileUrl = encodeURIComponent( globalConfig.baseUrl + '/api/attachment?name=' + options.name + '&url=' + options.url) this.allUrl = this.viewerUrl + '?file=' + fileUrl } } </script>
效果nginx
显示正常git
显示模糊,而且中文显示不全,其中模糊问题是模拟器缘由;可是中文显示问题是真,调试出现两个警告。第二个警告pdf.js默认不显示电子签章(数字签名)问题,查了不少资料也没解决,各位码友有遇到过而且解决了吗?
<img src="https://user-gold-cdn.xitu.io...;h=812&f=png&s=253098" width="360" align=center />github
出现跨域问题,而且调试出现没法访问pdf.js国际化文件
<img src="https://user-gold-cdn.xitu.io...;h=452&f=png&s=95345" width="360" align=center />web
基于Android和iOS预览出现的各类问题,最根本缘由是viewer.html文件放到前端致使加载资源文件丢失问题。针对这个问题,我就在想能不能直接放在spring后端做为静态资源访问文件呢?因而有了下面的方法。
<img src="https://user-gold-cdn.xitu.io...;h=650&f=png&s=69156" width="400" align=center />
viewerUrl: globalConfig.baseUrl + '/pdf/web/viewer.html'
修改后效果
<img src="https://user-gold-cdn.xitu.io...;h=698&f=png&s=184270" width="360" align=center />
模糊是模拟器缘由,在真机上测试经过
<img src="https://user-gold-cdn.xitu.io...;h=634&f=png&s=140599" width="360" align=center />
配置tomcat的config目录下的server.xml,在最后的<server></server>中间添加以下:
port=8090 文件访问服务端口
docBase="/root/" 文件存储目录
服务器上文件会存储到/root/fileData/目录下
文件访问地址为: http://ip地址:8090/fileData/...
<Service name="fileData"> <!--分配8089端口 --> <!-- <Connector port="8090" protocol="HTTP/1.1" connectionTimeout="20000" URIEncoding="GBK" redirectPort="8443" /> --> <Connector port="8090" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" /> <Engine name="fileData" defaultHost="localhost"> <!--name为项目访问地址 此配置的访问为http://localhost:8080 appBase配置tomcat下wabapps下的路径--> <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false"> <!--资源地址--> <Context path="" docBase="/root/" debug="0" reloadable="false"/> </Host> </Engine> </Service>
直接上代码
读取目录文件,转换为 二进制流
前端组件file-preview.vue中 fileUrl为 /api/attachment
核心代码
ios = new FileInputStream(sourceFile); os = response.getOutputStream(); int read = 0; byte[] buffer = new byte[1024 * 1024]; while ((read = ios.read(buffer)) != -1) { os.write(buffer, 0, read); } os.flush();
完整代码
@RequestMapping(value = "/api/attachment", method = RequestMethod.GET) public void getFileBytes(@RequestParam("name") String name, @RequestParam("url") String url, HttpServletRequest request, HttpServletResponse response) { response.reset(); response.setContentType("application/octet-stream"); response.setCharacterEncoding("utf-8"); response.setHeader("Content-Disposition", "attachment;filename=" + name); AttachmentVO attachmentVO = new AttachmentVO(); FileInputStream ios = null; OutputStream os = null; try { name = CharsetUtils.toUTF_8(name); url = CharsetUtils.toUTF_8(url); attachmentVO.setUrl(url); attachmentVO.setName(name); File sourceFile = getDictionaryFile(attachmentVO, request); if (null == sourceFile) { // throw new HttpResponseException(300, "附件不存在!"); return; } /** * 判断文件类型 */ /* 得到文件名后缀 */ String ext = ""; if (!"".equals(url) && url.contains(".")) { ext = url.substring(url.lastIndexOf(".") + 1, url.length()).toUpperCase(); } /* 根据文件类型不一样进行预览 */ /* 预览pdf */ if ("PDF".equals(ext)) { response.setContentType("application/pdf"); } /** * 将文件写入输出流,显示在界面上,实现预览效果 */ ios = new FileInputStream(sourceFile); os = response.getOutputStream(); int read = 0; byte[] buffer = new byte[1024 * 1024]; while ((read = ios.read(buffer)) != -1) { os.write(buffer, 0, read); } os.flush(); } catch (Exception e) { e.printStackTrace(); try { if (null != ios) { ios.close(); } if (null != os) { os.close(); } } catch (IOException ex) { ex.printStackTrace(); } } }
原理:
搭建 OpenOffice服务,将文件转换为pdf,在使用pdf.js预览
tar xzvfm Apache_OpenOffice_xxx.tar.gz cd zh-CN/RPMS rpm -ivh *rpm
# 127.0.0.1只能本机使用该服务 /opt/openoffice4/program/soffice "-accept=socket,host=127.0.0.1,port=8100;urp;" -headless -nofirststartwizard & # 0.0.0.0远程ip能使用 /opt/openoffice4/program/soffice "-accept=socket,host=0.0.0.0,port=8100;urp;" -headless -nofirststartwizard &
<!-- openoffice start --> <dependency> <groupId>org.openoffice</groupId> <artifactId>juh</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>org.openoffice</groupId> <artifactId>jurt</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>org.openoffice</groupId> <artifactId>ridl</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>org.openoffice</groupId> <artifactId>unoil</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>com.artofsolving</groupId> <artifactId>jodconverter</artifactId> <version>2.2.2</version> </dependency> <!-- openoffice end -->
<span style="color:red">注意</span>:jodconverter须要单独下载2.2.2版本,以前的版本都不行,并且maven中央仓库没有2.2.2版本。而后再单独导入。下载地址:https://sourceforge.net/proje...
单独导入
mvn install:install-file -Dfile="jodconverter-2.2.2.jar" -DgroupId=com.artofsolving -DartifactId=jodconverter -Dversion=2.2.2 -Dpackaging=jar
核心代码
connection = new SocketOpenOfficeConnection(openofficeHost, openofficePort); connection.connect(); DocumentConverter converter = new StreamOpenOfficeDocumentConverter(connection); converter.convert(sourceFile, pdfFile);
完整代码
/* 利用openOffice将office文件转换为pdf格式, 而后预览doc, docx, xls, xlsx, ppt, pptx */ if ("DOC".equals(ext) || "DOCX".equals(ext) || "XLS".equals(ext) || "XLSX".equals(ext) || "PPT".equals(ext) || "PPTX".equals(ext)) { /* filePath在数据库中是不带文件后缀的, 因为jodConverter必需要识别后缀,因此将服务器中的文件重命名为带后缀的文件 */ // File docFileWithExt = new File(filePath + "." + ext.toLowerCase()); //带后缀的文件 // docFile.renameTo(docFileWithExt); /* 转换以后的文件名 */ String filePath = sourceFile.getPath(); File pdfFile; if (filePath.contains(".")) { pdfFile = new File(filePath.substring(0, filePath.lastIndexOf(".")) + ".pdf"); } else { pdfFile = new File(filePath + ".pdf"); } /* 判断即将要转换的文件是否真实存在 */ if (sourceFile.exists()) { /* 判断该文件是否已经被转换过,若已经转换则直接预览 */ if (!pdfFile.exists()) { OpenOfficeConnection connection; /* 打开OpenOffice链接 */ try { connection = new SocketOpenOfficeConnection(openofficeHost, openofficePort); connection.connect(); } catch (java.net.ConnectException e) { log.warn("openOffice未链接,正在从新链接..."); // 启动OpenOffice的服务 String command = openofficeInstallPath + "program/soffice -headless -accept=\"socket,host=127.0.0.1,port=8100;urp;\" -nofirststartwizard"; Runtime.getRuntime().exec(command); Thread.sleep(1000); connection = new SocketOpenOfficeConnection(8100); connection.connect(); log.warn("openOffice从新链接成功!!!"); } try { // DocumentConverter converter = new OpenOfficeDocumentConverter(connection); DocumentConverter converter = new StreamOpenOfficeDocumentConverter(connection); converter.convert(sourceFile, pdfFile); connection.disconnect(); // filePath = pdfFile.getPath(); // 文件转换以后的路径 sourceFile = pdfFile; response.setContentType("application/pdf"); } catch (OpenOfficeException e) { e.printStackTrace(); // 读取转换文件失败 log.info("读取转换文件失败!!!"); return; } finally { // 发生exception时, connection不会自动切断, 程序会一直挂着 try { if (connection != null) { connection.disconnect(); } } catch (Exception e) { e.printStackTrace(); } } } else { // filePath = pdfFile.getPath(); // 文件已经转换过 sourceFile = pdfFile; response.setContentType("application/pdf"); } } else { log.info("须要预览的文档在服务器中不存在!!!"); // 文件不存在,直接返回 return; } }
/* 预览图片 */ if ("PNG".equals(ext) || "JPEG".equals(ext) || "JPG".equals(ext)) { response.setContentType("image/jpeg"); } /* 预览BMP格式的文件 */ if ("BMP".equals(ext)) { response.setContentType("image/bmp"); } /* 预览GIF格式的文件 */ if ("GIF".equals(ext)) { response.setContentType("image/gif"); }
采用uni-app的 uni.previewImage接口
fileUrl:为 文件流访问地址
// 预览图片 uni.previewImage({ urls: [fileUrl], longPressActions: { itemList: ['发送给朋友', '保存图片', '收藏'], success: function(data) { console.log('选中了第' + (data.tapIndex + 1) + '个按钮,第' + (data.index + 1) + '张图片'); }, fail: function(err) { console.log(err.errMsg); } } })
@RequestMapping(value = "/api/attachment", method = RequestMethod.GET) public void getFileBytes(@RequestParam("name") String name, @RequestParam("url") String url, HttpServletRequest request, HttpServletResponse response) { response.reset(); // 解决IFrame拒绝的问题,无效 // response.setHeader("X-Frame-Options", "SAMEORIGIN"); response.setContentType("application/octet-stream"); response.setCharacterEncoding("utf-8"); response.setHeader("Content-Disposition", "attachment;filename=" + name); AttachmentVO attachmentVO = new AttachmentVO(); FileInputStream ios = null; OutputStream os = null; try { name = CharsetUtils.toUTF_8(name); url = CharsetUtils.toUTF_8(url); attachmentVO.setUrl(url); attachmentVO.setName(name); File sourceFile = getDictionaryFile(attachmentVO, request); if (null == sourceFile) { // throw new HttpResponseException(300, "附件不存在!"); return; } /** * 判断文件类型 */ /* 得到文件名后缀 */ String ext = ""; if (!"".equals(url) && url.contains(".")) { ext = url.substring(url.lastIndexOf(".") + 1, url.length()).toUpperCase(); } /* 根据文件类型不一样进行预览 */ /* 预览图片 */ if ("PNG".equals(ext) || "JPEG".equals(ext) || "JPG".equals(ext)) { response.setContentType("image/jpeg"); } /* 预览BMP格式的文件 */ if ("BMP".equals(ext)) { response.setContentType("image/bmp"); } /* 预览GIF格式的文件 */ if ("GIF".equals(ext)) { response.setContentType("image/gif"); } /* 预览pdf */ if ("PDF".equals(ext)) { response.setContentType("application/pdf"); } /* 利用openOffice将office文件转换为pdf格式, 而后预览doc, docx, xls, xlsx, ppt, pptx */ if ("DOC".equals(ext) || "DOCX".equals(ext) || "XLS".equals(ext) || "XLSX".equals(ext) || "PPT".equals(ext) || "PPTX".equals(ext)) { /* filePath在数据库中是不带文件后缀的, 因为jodConverter必需要识别后缀,因此将服务器中的文件重命名为带后缀的文件 */ // File docFileWithExt = new File(filePath + "." + ext.toLowerCase()); //带后缀的文件 // docFile.renameTo(docFileWithExt); /* 转换以后的文件名 */ String filePath = sourceFile.getPath(); File pdfFile; if (filePath.contains(".")) { pdfFile = new File(filePath.substring(0, filePath.lastIndexOf(".")) + ".pdf"); } else { pdfFile = new File(filePath + ".pdf"); } /* 判断即将要转换的文件是否真实存在 */ if (sourceFile.exists()) { /* 判断该文件是否已经被转换过,若已经转换则直接预览 */ if (!pdfFile.exists()) { OpenOfficeConnection connection; /* 打开OpenOffice链接 */ try { connection = new SocketOpenOfficeConnection(openofficeHost, openofficePort); connection.connect(); } catch (java.net.ConnectException e) { log.warn("openOffice未链接,正在从新链接..."); // 启动OpenOffice的服务 String command = openofficeInstallPath + "program/soffice -headless -accept=\"socket,host=127.0.0.1,port=8100;urp;\" -nofirststartwizard"; Runtime.getRuntime().exec(command); Thread.sleep(1000); connection = new SocketOpenOfficeConnection(8100); connection.connect(); log.warn("openOffice从新链接成功!!!"); } try { // DocumentConverter converter = new OpenOfficeDocumentConverter(connection); DocumentConverter converter = new StreamOpenOfficeDocumentConverter(connection); converter.convert(sourceFile, pdfFile); connection.disconnect(); // filePath = pdfFile.getPath(); // 文件转换以后的路径 sourceFile = pdfFile; response.setContentType("application/pdf"); } catch (OpenOfficeException e) { e.printStackTrace(); // 读取转换文件失败 log.info("读取转换文件失败!!!"); return; } finally { // 发生exception时, connection不会自动切断, 程序会一直挂着 try { if (connection != null) { connection.disconnect(); } } catch (Exception e) { e.printStackTrace(); } } } else { // filePath = pdfFile.getPath(); // 文件已经转换过 sourceFile = pdfFile; response.setContentType("application/pdf"); } } else { log.info("须要预览的文档在服务器中不存在!!!"); // 文件不存在,直接返回 return; } } /** * 将文件写入输出流,显示在界面上,实现预览效果 */ ios = new FileInputStream(sourceFile); os = response.getOutputStream(); int read = 0; byte[] buffer = new byte[1024 * 1024]; while ((read = ios.read(buffer)) != -1) { os.write(buffer, 0, read); } os.flush(); } catch (Exception e) { e.printStackTrace(); try { if (null != ios) { ios.close(); } if (null != os) { os.close(); } } catch (IOException ex) { ex.printStackTrace(); } } }
赞助做者