cordova插件之下载文件并打开
补充更新
cordova-plugin-file-opener2
插件在[email protected]
上会报编译错误,换成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被版本搞的焦头烂额,希望能给各位看官一点帮助~
如有疑问,欢迎沟通~