1024刚过,也祝愿各位码友在今后生活中,身体健康,事事顺心,再无Bug。
之前写过一篇文章关于上传目录文件:uni-app系统目录文件上传(非只图片和视频)解决方案,这次来解决文件预览问题。
uni-app 是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、Android、H5、以及各种小程序(微信/支付宝/百度/头条/QQ/钉钉)等多个平台。在做业务系统时,不可避免会遇到文件在线预览的需求。这里的文件包括PDF、Word、Excel、PPT、图片等。而在线预览不是下载后再用本地软件或浏览器打开预览,而是直接通过文件流的形式查看。本方案主要解决在线预览问题,以及在uni-app开发过程中遇到一系列问题。
如果有欠缺的地方,或者有更好的方案,还望各位码友多提意见,多多交流,文章最后可以加我。
文件预览,首先会想到pdf预览,前端做pdf预览,首先也会想到pdf.js,那我们就从pdf.js说起。
pdf.js开源地址和在线例子
Github
Online Demo
新建vue组件file-preview.vue
<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>
效果
解决
基于Android和iOS预览出现的各种问题,最根本原因是viewer.html文件放到前端导致加载资源文件丢失问题。针对这个问题,我就在想能不能直接放在spring后端作为静态资源访问文件呢?于是有了下面的方法。
在基于spring mvc的后端代码中,将插件包的build和web文件夹放到webapp下面(新建pdf文件夹),spring boot架构的后端项目同理,放到静态资源目录
修改前端组件file-preview.vue中的viewerUrl,其中globalConfig.baseUrl为代理后端地址的baseUrl。如Vue中proxyTable或nginx代理
viewerUrl: globalConfig.baseUrl + '/pdf/web/viewer.html'
配置tomcat的config目录下的server.xml,在最后的
port=8090 文件访问服务端口
docBase="/root/" 文件存储目录
服务器上文件会存储到/root/fileData/目录下
文件访问地址为:http://ip地址:8090/fileData/文件名
<Service name="fileData">
<Connector port="8090" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
<Engine name="fileData" defaultHost="localhost">
<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预览
下载Apache_OpenOffice
解压
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 &
<dependency>
<groupId>org.openofficegroupId>
<artifactId>juhartifactId>
<version>4.1.2version>
dependency>
<dependency>
<groupId>org.openofficegroupId>
<artifactId>jurtartifactId>
<version>4.1.2version>
dependency>
<dependency>
<groupId>org.openofficegroupId>
<artifactId>ridlartifactId>
<version>4.1.2version>
dependency>
<dependency>
<groupId>org.openofficegroupId>
<artifactId>unoilartifactId>
<version>4.1.2version>
dependency>
<dependency>
<groupId>com.artofsolvinggroupId>
<artifactId>jodconverterartifactId>
<version>2.2.2version>
dependency>
注意:jodconverter需要单独下载2.2.2版本,之前的版本都不行,而且maven中央仓库没有2.2.2版本。然后再单独导入。下载地址:https://sourceforge.net/projects/jodconverter/files/
单独导入
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();
}
}
}
转载请注明:溜爸 » 跨平台(uni-app)文件在线预览解决方案