vue+cordova混合开发APP中向手机本地存储/读取文件

项目场景:

采用 vue + cordova 开发的App, 项目首屏为 three.js 编写的3D场景


问题描述

3D模型的数据是首屏启动后前端调用接口获取的json数据,数据大小30M.

用户在首页与其他页面切换时,调用该接口时间过长,并消耗大量流量

解决思路:

  1. 首页页面不销毁,做缓存. (可行,但three.js的页面内存占用量极大,不做特殊处理的话会导致其他页面卡顿,并造成app崩溃闪退)
  2. 将3D模型数据一并打入app,从项目资源中读取(缺点:3D模型更新时需要重新发版App.不能及时获取到最新的3D模型)
  3. 首次调用接口后,将3D模型数据缓存到手机文件中,之后再次获取时,从手机文件中读取.(缺点:根据手机性能,读取也存在卡顿问题,部分手机读取大文件会闪退,可以将数据切片处理解决该问题)

实现步骤:

这里主要记录 如何利用 cordova  cordova-plugin-file 插件 向手机存储/读取文件,其他逻辑省略

在cordova项目根目录下 安装 cordova-plugin-file 插件

cordova plugin add cordova-plugin-file 

 安装之后,在cordova项目下, plugins文件下, 会有该插件

编写 写入文件方法

// 写入文件
    writeFileLast(dataObj, name) { //dataObj为你要存入为文件,//name是我自己用,根据自己情况使用
      return new Promise((resolve, reject) => { //因为是异步 我加了promise 方便同步代码
        window.resolveLocalFileSystemURL(cordova.file.externalDataDirectory,
          (dir) => { 
            //dir大概是你存文件的那个文件夹 调用getFile根据参数获取文件夹下的文件
            console.log("文件路径: " + cordova.file.externalDataDirectory);
            dir.getFile(`${name}.txt`, { create: true }, 
              //第一个参数是你想获取的文件名字
              //第二个参数 为true的时候 意味着如果在此文件夹下没有读取到对应名字的文件,文件夹下 
              创建一个以第一个参数为名字的空白文件
              (fileEntry) => {
                  //fileEntry是上一个步骤获取的文件
                  fileEntry.createWriter(function (fileWriter) {
                  //写入成功的回调
                  fileWriter.onwriteend = function () {
                    console.log("Successful file write...");
                    resolve()
                  };
                  //写入失败的回调
                  fileWriter.onerror = function (e) {
                    console.log("Failed file write: " + e.toString());
                    reject("Failed file write: " + e.toString())
                  };
                  // 如果dataObj是null,则创建一个新的文件
                  if (!dataObj) {
                    dataObj = new Blob(['我是一个测试文件'], { type: 'text/plain' });
                  }
                  // 写入内容
                  fileWriter.write(dataObj);
                })

              },
              (err) => { console.log('getFile错误', err) }
            );
          }, (err) => { console.log('resolveLocalFileSystemURL错误', err); });
      })
    },

 编写 读取文件方法

// 读取文件
    readFileLast(name) { //name 要读取的文件名字
      //let _this = this
      return new Promise((resolve, reject) => {
        window.resolveLocalFileSystemURL(cordova.file.externalDataDirectory,
          // 成功回调
          (dir) => {  //和读取参数大同小异 以下不在赘述
            console.log("文件存储路径: " + cordova.file.externalDataDirectory);
            //在该目录下获取 一个基础变量 所有文件操作基于此变量
            dir.getFile(`${name}.txt`, { create: false },
              // 成功回调
              (fileEntry) => {
                console.log("指定目录下获取到相关文件 --基础文件信息", fileEntry);
                fileEntry.file(function (file) {
                  let reader = new FileReader(); //创建一个读取器
                  reader.onloadend = (e) => {
                    console.log('文件信息:' + fileEntry)
                    console.log('文件信息:' + file)
                    // console.log('内容', reader.result);
                    //_this.isLoad = true;
                    resolve(reader.result) //将结果送出去
                  }
                  reader.readAsText(file);           //读取文本类型文件使用
                  // reader.readAsArrayBuffer(file); //读取二进制文件 (大概吧,我忘记了~)

                })
              },
              // 失败回调
              (err) => {
                console.log('getFile错误', err)
                //this.isLoad = false;
                // 失败 但保证要继续运行
                resolve(false)
              }
            );
          },
          // 失败回调
          (err) => { console.log('resolveLocalFileSystemURL错误', err); });
      })
    },

最后,还有一点就是,性能差的手机,读取10M以上的数据,都可能会有闪退现象,建议存文件的时候,先进行切片处理. 分享一下我的切片思路

// 包体拆分
    splitFile(obj) { //obj是我要存的文件 数据格式是JSON 
      let data = JSON.stringify(obj);   //先序列化成json字符串
      if (data.length > 10000 * 1000) { //大概大于10M 
        let num = Math.ceil(data.length / (10000 * 1000)); //拆分份数
        let arr = [];
        for (let i = 0; i < num; i++) {
          let str = data.slice(i * 10000 * 1000, (i + 1) * 10000 * 1000);
          arr.push(str);
        }
        console.log('json数据拆分后的字符串数组', arr);
        this.config.num = arr.length //这个config 是一个记录配置信息 记录了文件切割了多少份 以 
        及文件的名字,到时候从本地读取的时候 要根据config里文件名字 + 份数索引  循环从本地读取
        arr.forEach((item, index) => {
          //给每份切割包 一个文件名字
          let fileName = this.config.upFileName.replace('.json', '') + index
          this.writeFileLast(item, fileName);
        })

      } else { //小于10M 直接存  不拆分
        let fileName = this.config.upFileName.replace('.json', '') + 0
        this.writeFileLast(data, fileName);
      }
      // 配置文件  这个文件一并存入手机
      let fileName = this.config.upFileName.replace('.json', '')
      let file = JSON.stringify(this.config)
      console.log('存配置文件 json', file);
      this.writeFileLast(file, fileName);
    },

    //读取的时候 先读取手机的config配置文件  从里面获取 文件名字 还有份数
    //循环读取 文件名字+索引  读取出每个分包
    //之后将分包 通过js合并 最后在序列化 JSON.parse()转换回来

    // 循环读取文件
    loadFile() {
      let fileList = []
      // 读取所有分包文件
      for (let i = 0; i < this.config.num; i++) {
        let fileName = this.config.upFileName.replace('.json', '') + i
        let fileItem = this.readFileLast(fileName)
        fileList.push(fileItem)
      }
      // 全部读取完毕 且没有异常
      Promise.all(fileList).then(async res => {
        console.log('全部读取完毕');
        // 把分包拼成总包
         let result = {
          data: JSON.parse(res.join(''))
        }
        console.log('拼接后的总包', result);
        if (this.isLoad) {//保证全部读取成功
          this.create3D(result)
        } else {
          console.log('有文件读取失败');
        }
      })
    }
    

成功存入后,进入你的手机文件 Andriod->Data->你的包名(com.开头的,参考我的下图)->files文件夹下,就是我们刚才存入的文件了

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

白云苍狗い

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值