Better

Ethan的博客,欢迎访问交流

Ionic中远程图片缓存组件

最近公司业务奇葩,什么要求都没网也可以使用,还要给用户一种有网的感觉,保证页面数据同步与后台提交,心里真是苦的一匹,今天针对远程图片的离线突然有一个很棒的想法,可以实现远程图片缓存,本地有图片时减少服务器请求,并且与具体业务脱离。

工具类准备

pathUtil

得到可读写路径,不同平台获取路径的方式不一样,代码如下:

.factory('pathUtil', [function () {
    return {
      getBasePath: function () {
        var basePath;
        if (ionic.Platform.isIOS()) {
          basePath = cordova.file.documentsDirectory + "EFOS/";
        } else {
          basePath = cordova.file.externalApplicationStorageDirectory;
        }
        return basePath;
      }
    }
  }])

fsUtil

实现文件的下载,检查文件是否已经存在,实现图片缓存暂时只需要这两个功能,代码如下:

.factory('fsUtil', ['pathUtil', '$q', '$cordovaFile', function (pathUtil, $q, $cordovaFile) {
    return {
      download: function (serverPath, downloadPathSuffix) {
        var defer = $q.defer();
        var fileTransfer = new FileTransfer();
        var basePath = pathUtil.getBasePath();
        var targetPath = basePath + '/' + downloadPathSuffix;
        fileTransfer.download(encodeURI(serverPath), targetPath,
          function (entry) {
            defer.resolve(entry.toURL());
          },
          function (error) {
            defer.reject(error);
          }, null);
        return defer.promise;
      },
      checkFile: function (path, filename) {
        var defer = $q.defer();
        window.resolveLocalFileSystemURL(path, function (fileEntry) {
          $cordovaFile.checkFile(fileEntry.toInternalURL(),filename).then(function () {
            defer.resolve(true);
          },function (error) {
            defer.resolve(false);
          });
        });
        return defer.promise;
      }
    }
  }])

imgLoader组件

使用localstorage存储远程与本地图片的对应关系,如果不存在则下载,然后本地新增K-V值,如果存在还需要判断是否已经被用户删除,先检查文件是否文件,不存在则下载。同时有个细节就是改变原来远程图片对象的属性值为本地路径。因为项目中还有个图片点击放大预览的功能。代码如下:

angular.module('starter.directive')
  .directive('imgLoader', ['fsUtil', 'md5', function (fsUtil, md5) {
    return {
      restrict: 'E',
      replace: true,
      template: '<img ng-src="{{url}}"/>',
      scope: {
        imgSrc: '='
      },
      link: function (scope, element, attrs, controller) {

        function init() {
          // 远程路径
          var url = scope.imgSrc;
          if(window.cordova){
            // 文件格式
            var format = url.substring(url.lastIndexOf('.'));
            // 本地文件名称
            var localFileName = md5.createHash(url) + format;

            var local = localStorage.getItem("cachedImgList");
            var cachedImgList = local ? JSON.parse(local) : {};
            var localFileUrl = cachedImgList[url];
            if (localFileUrl) {
              // 本地有,检查文件是否已经被删除
              var index = localFileUrl.lastIndexOf('/');
              var filePath = localFileUrl.substring(0, index + 1);
              var fileName = localFileUrl.substring(index + 1);
              fsUtil.checkFile(filePath, fileName).then(function (isExist) {
                if (isExist) {
                  scope.imgSrc = scope.url = localFileUrl;
                } else {
                  scope.imgSrc = scope.url = url;
                  delete cachedImgList[url];
                  download(url, localFileName, cachedImgList);
                }
              })
            } else {
              scope.imgSrc = scope.url = url;
              // 本地无,直接下载
              download(url, localFileName, cachedImgList);
            }
          } else {
            scope.imgSrc = scope.url = url;
          }
        }

        function download(url, localFileName, cachedImgList) {
          fsUtil.download(url, 'imgCacheDir/' + localFileName).then(function (localUrl) {
            cachedImgList[url] = localUrl;
            scope.imgSrc = scope.url = localUrl;
            localStorage.setItem("cachedImgList", JSON.stringify(cachedImgList));
          });
        }

        init();

      }
    };
  }]);

总结

个人觉得对于一个基于webView的Hybrid App,离线使用的要求确实是有点过分的,更过分的是还要求本地数据在用户操作了之后要同步显示,这无疑增加了很多的工作量,而且开发繁琐易出错,上述图片离线的思路还是很不错的,最大的优点就是与复杂的业务脱离,提高了通用型且不易出错。然后对于非二进制数据请求的缓存,暂时没有想到良好的方式能做到和业务脱离,而且使用localstorage的简单K-V存储,不能存储结构性数据,且容量只有5M,也不是一个优秀的本地存储方式。



留言