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();
}
}
}
复制代码