ionic项目中调用本地应用打开请求的网络word、pdf、txt等文件

一、实现思路

我们在写插件之前需要想清楚:
1. 网络资源只是一个请求链接不是真正的文件
2. 调用本地应用打开文件,这个文件是不是需要真实存在本地,才能打开。
3. 如果需要存在本地目录,那么应该存在什么样的目录。
想清楚之后我们来实现:
首先我来说下我的实现思路:
点击请求链接 —> 获取请求链接 —> 根据链接下载网络资源到本地 —> 调用自定义插件 —> 调用本地应用打开下载到本地的资源文件

二、动手实践
1. 编写自定义的插件:

1.使用plugman创建自己的自定义插件,并添加android平台
2.编辑调用的openFile.js

var exec = require('cordova/exec');
exports.openFile = function (param, success, error) {
    exec(success, error, 'OpenFilePlugin', 'openFile', [param]);
};

3.编辑实现的功能的java类

package com.ai.ced.openfile;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.support.v4.content.FileProvider;

import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CallbackContext;

import org.json.JSONArray;
import org.json.JSONException;

import java.io.File;

/**
 * This class echoes a string called from JavaScript.
 */
public class OpenFilePlugin extends CordovaPlugin {

    public static Activity activity;

    @Override
    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
        if (action.equals("openFile")) {
            String message = args.getString(0);
            this.openFile(message, callbackContext);
            return true;
        }
        return false;
    }

    private void openFile(String message, CallbackContext callbackContext) {
        this.activity = this.cordova.getActivity();
//        this.openAndroidFile("/storage/emulated/0/ReleaseChannel.txt");

        this.openAndroidFile(message);
    }

    public  void openAndroidFile(String filepath){
        Intent intent = new Intent();
        File file = new File(filepath);
//        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//设置标记
        Uri uri = FileProvider.getUriForFile(activity,
        BuildConfig.APPLICATION_ID + ".fileProvider",
        file);

        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.setAction(Intent.ACTION_VIEW);//动作,查看
//        intent.setDataAndType(Uri.fromFile(file), this.getMIMEType(file));//设置类型
        intent.setDataAndType(uri, this.getMIMEType(file));//设置类型
        this.activity.startActivity(intent);
    }


    private String getMIMEType(File file) {

        String type="*/*";
        String fName = file.getName();
        //获取后缀名前的分隔符"."在fName中的位置。
        int dotIndex = fName.lastIndexOf(".");
        if(dotIndex < 0)
            return type;
        /* 获取文件的后缀名 */
        String fileType = fName.substring(dotIndex,fName.length()).toLowerCase();
        if(fileType == null || "".equals(fileType))
            return type;
        //在MIME和文件类型的匹配表中找到对应的MIME类型。
        for(int i=0;i<MIME_MapTable.length;i++){
            if(fileType.equals(MIME_MapTable[i][0]))
                type = MIME_MapTable[i][1];
        }
        return type;
    }


    private static final String[][] MIME_MapTable={
            //{后缀名,    MIME类型}
            {".3gp",    "video/3gpp"},
            {".apk",    "application/vnd.android.package-archive"},
            {".asf",    "video/x-ms-asf"},
            {".avi",    "video/x-msvideo"},
            {".bin",    "application/octet-stream"},
            {".bmp",      "image/bmp"},
            {".c",        "text/plain"},
            {".class",    "application/octet-stream"},
            {".conf",    "text/plain"},
            {".cpp",    "text/plain"},
            {".doc",    "application/msword"},
            {".docx",    "application/msword"},
            {".exe",    "application/octet-stream"},
            {".gif",    "image/gif"},
            {".gtar",    "application/x-gtar"},
            {".gz",        "application/x-gzip"},
            {".h",        "text/plain"},
            {".htm",    "text/html"},
            {".html",    "text/html"},
            {".jar",    "application/java-archive"},
            {".java",    "text/plain"},
            {".jpeg",    "image/jpeg"},
            {".JPEG",    "image/jpeg"},
            {".jpg",    "image/jpeg"},
            {".js",        "application/x-javascript"},
            {".log",    "text/plain"},
            {".m3u",    "audio/x-mpegurl"},
            {".m4a",    "audio/mp4a-latm"},
            {".m4b",    "audio/mp4a-latm"},
            {".m4p",    "audio/mp4a-latm"},
            {".m4u",    "video/vnd.mpegurl"},
            {".m4v",    "video/x-m4v"},
            {".mov",    "video/quicktime"},
            {".mp2",    "audio/x-mpeg"},
            {".mp3",    "audio/x-mpeg"},
            {".mp4",    "video/mp4"},
            {".mpc",    "application/vnd.mpohun.certificate"},
            {".mpe",    "video/mpeg"},
            {".mpeg",    "video/mpeg"},
            {".mpg",    "video/mpeg"},
            {".mpg4",    "video/mp4"},
            {".mpga",    "audio/mpeg"},
            {".msg",    "application/vnd.ms-outlook"},
            {".ogg",    "audio/ogg"},
            {".pdf",    "application/pdf"},
            {".png",    "image/png"},
            {".pps",    "application/vnd.ms-powerpoint"},
            {".ppt",    "application/vnd.ms-powerpoint"},
            {".pptx",    "application/vnd.ms-powerpoint"},
            {".prop",    "text/plain"},
            {".rar",    "application/x-rar-compressed"},
            {".rc",        "text/plain"},
            {".rmvb",    "audio/x-pn-realaudio"},
            {".rtf",    "application/rtf"},
            {".sh",        "text/plain"},
            {".tar",    "application/x-tar"},
            {".tgz",    "application/x-compressed"},
            {".txt",    "text/plain"},
            {".wav",    "audio/x-wav"},
            {".wma",    "audio/x-ms-wma"},
            {".wmv",    "audio/x-ms-wmv"},
            {".wps",    "application/vnd.ms-works"},
            //{".xml",    "text/xml"},
            {".xml",    "text/plain"},
            {".z",        "application/x-compress"},
            {".zip",    "application/zip"},
            {"",        "*/*"}
    };
}

到这主要的功能就已经完成了。

三、怎么调用
1.在ionic项目中需要配合File、FileTransfer进行使用。既然需要组合,那我们写一个工具service。
import { Injectable } from '@angular/core';
import { FileTransferObject, FileTransfer } from '@ionic-native/file-transfer';
import { File } from '@ionic-native/file';
import { CommonUtilService } from './commonUtilService';

declare let OpenFilePlugin: any;

/*
  Generated class for the WechatShareServiceProvider provider.

  See https://angular.io/guide/dependency-injection for more info on providers
  and Angular DI.
*/

@Injectable()
export class OpenFileService {

  constructor(private transfer: FileTransfer,
              private comUtil: CommonUtilService,
              private file: File) {}


  openFile(url: string){
    console.log("OpenFileService enter");
    const fileTransfer: FileTransferObject = this.transfer.create();
    // const url = 'http://www.gz10010.shop/resource/download/1111111111111111111111.pdf';
    // 获取文件名1111111111111111111111.pdf
    let tempUrl = url.substr(url.lastIndexOf("/")+1);
    // 获取存储路径
    // this.file.externalCacheDirectory获取对应app的缓存路径file:///storage/emulated/0/Android/data/io.ionic.starter/cache/
    // 需要去掉 file:/// 如果不去掉会报路径找不到该路径
    // /storage/emulated/0/Android/data/io.ionic.starter/cache/ 正确路径
    let storeUrl = this.file.externalCacheDirectory.substr(7);
    fileTransfer.download(url, storeUrl + tempUrl).then( (data) => {
      // 拼接打开的文件路径
      let openUrl = storeUrl + tempUrl;
      OpenFilePlugin.openFile(openUrl);
    }).catch( err => {
      this.comUtil.showAlert({title: "文件下载失败", message: JSON.stringify(err)});
    });
  }
}
2. 业务调用

在这里插入图片描述
在这里插入图片描述

四、问题解决
1. 真机下:android.os.FileUriExposedException:exposed beyond app through Intent.getData()

方案一:
1.在AndroidManifest.xml文件的application里添加

<provider

               android:name="android.support.v4.content.FileProvider"

               android:authorities="app的包名.fileProvider"

               android:grantUriPermissions="true"

               android:exported="false">

              <meta-data

                                android:name="android.support.FILE_PROVIDER_PATHS"

                                android:resource="@xml/file_paths"/>

</provider>


2.在res目录下新建一个xml文件夹,并且新建一个file_paths的xml文件
3.打开file_paths.xml文件添加如下内容

<?xml version="1.0" encoding="utf-8"?>
<paths>
             <external-path path="Android/data/app的包名/" name="files_root"/>
             <external-path path="." name="external_storage_root"/>
</paths>

方案二:
在MainActivity的onCreate方法中添加如下代码

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
            StrictMode.setVmPolicy(builder.build());
        }
2. 编译报错:Didn’t find class “android.support.v4.content.FileProvider” on path:

添加v4依赖包,重新clean、rebuild
在这里插入图片描述

五、参考资源

FileProvider:
https://blog.csdn.net/u010773839/article/details/72886332
https://blog.csdn.net/fox_wei_hlz/article/details/78732907
https://www.cnblogs.com/netcorner/p/6542373.html
https://blog.csdn.net/z_x_Qiang/article/details/79940700
https://blog.csdn.net/acesheep_911/article/details/81708254
https://blog.csdn.net/biickvtkd/article/details/78803481
插件代码:
https://blog.csdn.net/u012527802/article/details/48464803
https://blog.csdn.net/luoyayun361/article/details/82746576

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值