补充更新
cordova-plugin-file-opener2
插件在android@7.0.0
上会报编译错误,换成cordova-plugin-vha-fileopener2
即可
前言
近期混合app项目中有文件预览的需求,因文件较多并涉及office、视频等文件格式,采用第三方app打开方案。
实现过程中出现一些android7.0+/8.0+的兼容性问题,特此记录。
采用此预览方案文件会被先下载到本地,cordova-plugin-file-opener2
插件其实可以直接打开网络地址来实现预览,采用此方式是基于以下考虑:
- 避免重复下载(因app中还有下载功能)
- 避免有文件格式解析错误的情况,用户可以到本地再次进行查看
- 下载目录可控
框架
项目采用cordova + VUE + MintUI
安装插件
cordova plugin add cordova-plugin-file
cordova plugin add cordova-plugin-file-transfer
cordova plugin add cordova-plugin-file-opener2
eg: cordova-plugin-file-opener
和cordova-plugin-file-opener2
这两个插件都可以打开文件,但cordova-plugin-file-opener2
支持cdvfile://
协议,兼容android7.0+
以上,不会出现文件权限的问题
一、确认API环境
采用Cordova开发的应用在运行的时候,Cordova提供的通过HTML5调用Native功能并不是立即就能使用的,Cordova框架在读入HTML5代码之后,要进行HTML5和Native建立桥接,在未能完成这个桥接的初始的情况下,是不能调用Native功能的。在Cordova框架中,当这个桥接的初始化完成后,会调用他自身特有的事件,即deviceready
事件。
deviceready事件是在每回读入HTML的时候都会被调用,而不只是应用启动时调用。
document.addEventListener("deviceready", function () {
// 现在可以安全的使用设备API
console.log('Device is Ready!')
}, false);
二、创建有效的文件路径
关于路径的详细解释可以查看文章:
官网API
cordova-plugin-file 文件操作整理系列
使用cordova-plugin-file
插件创建有效的文件路径
Device Path | cordova.file.* |
AndroidExtraFileSystems |
r/w? | persistent? | OS clears | private |
---|---|---|---|---|---|---|
file:///android_asset/ |
applicationDirectory | assets | r | N/A | N/A | Yes |
/data/data/<app-id>/ |
applicationStorageDirectory | - | r/w | N/A | N/A | Yes |
cache
|
cacheDirectory | cache | r/w | Yes | Yes* | Yes |
files
|
dataDirectory | files | r/w | Yes | No | Yes |
Documents
|
documents | r/w | Yes | No | Yes | |
<sdcard>/ |
externalRootDirectory | sdcard | r/w | Yes | No | No |
Android/data/<app-id>/
|
externalApplicationStorageDirectory | - | r/w | Yes | No | No |
cache
|
externalCacheDirectory | cache-external | r/w | Yes | No** | No |
files
|
externalDataDirectory | files-external | r/w | Yes | No | No |
- 当目标的WebView的客户(而不是浏览器)或本地应用程序(Windows),你不需要在使用持久性存储使用requestquota。
- 在沙盒目录结构中使用
window.requestFileSystem
- 获取或操作系统文件/目录,可以使用
window.resolveLocalFileSystemURL
Android7.0+
遇到 android.os.FileUriExposedException: file:///storage/emulated.. exposed beyond app through Intent.getData()
错误时,要使用window.resolveLocalFileSystemURL
和cordova.file.externalDataDirectory
,不要使用沙盒目录结构
/**
* desc: 创建文件方法
*/
window.resolveLocalFileSystemURL(
cordova.file.externalDataDirectory,
function(fs) {
fs.getFile(
_this.fileName, // 创建的文件名
{ create: true, exclusive: true },
// create:创建新文件,exclusive:文件已存在时抛出异常
function(fileEntry) {
// 创建成功回调下载方法写入文件
_this.downloadFile(fileEntry);
},
function(err) {
// 失败回调
// 重新读取文件并打开
fs.getFile(
_this.fileName,
{ create: false },
function(fileEntry) {
// 成功读取文件后调用cordova-plugin-file-opener2插件打开文件
_this.preView(fileEntry);
},
function(err) {
_this.toast('读取文件失败');
}
);
}
);
},
function(error) {
_this.toast('进入文件系统失败!');
}
);
三、下载文件
/**
* desc: 文件下载方法
*/
function downloadFile(fileEntry) {
// 初始化进度条并显示
// 此处采用mint-ui的Progress组件
_this.progress = 0;
_this.showProgress = true;
//实例化
let fileTransfer = new FileTransfer();
//监听下载进度
fileTransfer.onprogress = function(e) {
if (e.lengthComputable) {
let progress = e.loaded / e.total;
// 显示下载进度
_this.progress = (progress * 100).toFixed(2);
}
};
// 使用fileTransfer.download开始下载
fileTransfer.download(
encodeURI(_this.savePath), //uri网络下载路径
fileEntry.toURL(), //文件本地存储路径
function(entry) {
// 下载完成执行本地预览
if (_this.progress > 1 || _this.progress === 1) {
_this.showProgress = false;
entry.file(data => {
_this.preView(fileEntry);
// 此处data.type可以直接得到文件的MIME-TYPE类型
});
}
},
function(error) {
_this.toast('下载失败!');
}
);
}
四、打开文件
/**
* desc: 文件打开方法
*/
function preview(fileEntry){
// 调用cordova-plugin-file-opener2插件实现用第三方app打开文件
cordova.plugins.fileOpener2.showOpenWithDialog(
// 此处必须填写cdvfile://地址,不然android7.0+会报文件权限错误
fileEntry.toInternalURL(), //文件本地地址转cdvfile://地址
fileTypeArr[_this.fileType], //文件类型,这里我是写了一个mime-Type类型合集fileTypeArr来调用
function onSuccess(data) {
console.log('成功预览:' + fileURL);
},
function onError(error) {
_this.toast(
'出错!请在' + cordova.file.externalDataDirectory + '目录下查看'
);
}
);
}
五、Android8.0+打开apk文件权限问题
在Android8.0+
上打开apk
文件时会报错android.os.FileUriExposedException: file:///storage/emulated/0/test.apk exposed beyond app through Intent.getData()
根据cordova官网提示 在android 8.0+
上您的应用程序必须具有ACTION_INSTALL_PACKAGE
权限,需要在config.xml
添加如下配置:
<platform name="android">
<config-file parent="/manifest" target="AndroidManifest.xml" xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
</config-file>
</platform>
注意安装文件的路径:在Android 7之前,您只能从“外部”分区安装APK。例如,您可以从中安装cordova.file.externalDataDirectory
,但不能从中安装cordova.file.dataDirectory
。Android 7+没有这个限制。
并在AndroidManifest.xml
文件中修改SDK
版本
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="23" />
cordova/ionic默认配置16-26 经过多次试,只支持16-23的sdk 版本。版本再高就报以上错误。
结语
至此,Android5.0+已全部兼容。
Android的版本真的是一个大坑,第一次开发混合app被版本搞的焦头烂额,希望能给各位看官一点帮助~
如有疑问,欢迎沟通~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。