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