1024剛過,也祝願各位碼友在從此生活中,身體健康,事事順心,再無Bug。php
以前寫過一篇文章關於上傳目錄文件:uni-app系統目錄文件上傳(非只圖片和視頻)解決方案,此次來解決文件預覽問題。html
uni-app 是一個使用 Vue.js 開發全部前端應用的框架,開發者編寫一套代碼,可發佈到iOS、Android、H五、以及各類小程序(微信/支付寶/百度/頭條/QQ/釘釘)等多個平臺。在作業務系統時,不可避免會遇到文件在線預覽的需求。這裏的文件包括PDF、Word、Excel、PPT、圖片等。而在線預覽不是下載後再用本地軟件或瀏覽器打開預覽,而是直接經過文件流的形式查看。本方案主要解決在線預覽問題,以及在uni-app開發過程當中遇到一系列問題。前端
若是有欠缺的地方,或者有更好的方案,還望各位碼友多提意見,多多交流,文章最後能夠加我。vue
pdf.js開源地址和在線例子 Github Online Demoios
下載插件包,下載地址 nginx
解壓,拷貝build和web目錄到項目hybrid->html目錄下,參考uni-app中web-view用法 git
新建vue組件file-preview.vuegithub
<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>
複製代碼
效果web
解決 基於Android和iOS預覽出現的各類問題,最根本緣由是viewer.html文件放到前端致使加載資源文件丟失問題。針對這個問題,我就在想能不能直接放在spring後端做爲靜態資源訪問文件呢?因而有了下面的方法。
在基於spring mvc的後端代碼中,將插件包的build和web文件夾放到webapp下面(新建pdf文件夾),spring boot架構的後端項目同理,放到靜態資源目錄
在xml文件中配置靜態文件訪問
修改前端組件file-preview.vue中的viewerUrl,其中globalConfig.baseUrl爲代理後端地址的baseUrl。如Vue中proxyTable或nginx代理
viewerUrl: globalConfig.baseUrl + '/pdf/web/viewer.html'
複製代碼
配置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 -->
複製代碼
注意:jodconverter須要單獨下載2.2.2版本,以前的版本都不行,並且maven中央倉庫沒有2.2.2版本。而後再單獨導入。下載地址:sourceforge.net/projects/jo…
單獨導入
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();
}
}
}
複製代碼