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(); } } }
贊助做者