撒网要见鱼
撒网要见鱼
  • 发布:2016-01-13 14:20
  • 更新:2020-10-19 17:40
  • 阅读:27928

【分享】图片下载本地缓存时间戳显示图片方法

分类:HTML5+

参考文章
http://ask.dcloud.net.cn/article/256
http://ask.dcloud.net.cn/article/397

说明:为了方便,里面使用的图片来源是从上面文章中的源码项目获取的.
说明:参考了上面文章中的思路,然后自己重新写了一个较为完整的图片本地缓存显示工具.
功能
1.第一次显示图片时下载到本地,然后之后如果本地存在缓存(根据url),则显示本地缓存的图片
2. 基于plus的storage,对每一个图片的缓存进行控制管理,增加时间戳参数,来控制本地缓存的有效时间
3. 将所有的下载任务放入队列中,进行统一下载管理,增加最大并发请求数,防止一次性请求过多损耗性能
4. 加入了图片缓存池机制,对于同一个下载路径的图片不会多次下载,而是填入缓存池,下载后统一回调
5. 修改了图片本地路径的获取方法,摆脱第三方依赖
6. 重构了代码,并入开发框架中

使用方法

 * 外部API:注意,需要显示的图片需要有data-img-localcache这个属性(用来放目标url),dom需要是原生对象  
 * 清除所有图片缓存:MobileFrame.ImageUtil.ImageLoaderFactory.clearLoaderImgsCache(successCb,errorCb)  
 * 采取本地缓存显示所有图片:MobileFrame.ImageUtil.ImageLoaderFactory.setAllNetImgsWithLocalCache();  
 * 清除某一张图片的本地缓存:MobileFrame.ImageUtil.ImageLoaderFactory.clearNetUrlImgCache(src);  
 * 显示某一张图片:MobileFrame.ImageUtil.ImageLoaderFactory.setImgWidthLocalCache(dom,src);

源码

/**  
 * @description   移动开发框架  
 * @author dailc  dailc   
 * @version 1.0  
 * @time 2016-01-11 16:57:57  
 * 功能模块:只依赖于plus系统  
 * @see http://ask.dcloud.net.cn/people/%E6%92%92%E7%BD%91%E8%A6%81%E8%A7%81%E9%B1%BC  
 * 图片本地缓存模块********************************  
 * 1.本地缓存显示图片  
 * 2.增加storage,增加每一个本地缓存的有效时间戳  
 * 3.增加自定义设置方法,可以根据不同需求,对参数进行修改  
 * 4.采用下载队列进行下载管理,增加最大并发请求数,防止一次性请求过多损耗性能  
 * 5.修改了图片本地路径的获取方法,摆脱第三方依赖  
 * 6.重构代码  
 * 外部API:注意,需要显示的图片需要有data-img-localcache这个属性(用来放目标url)  
 * 清除所有图片缓存:MobileFrame.ImageUtil.ImageLoaderFactory.clearLoaderImgsCache(successCb,errorCb)  
 * 采取本地缓存显示所有图片:MobileFrame.ImageUtil.ImageLoaderFactory.setAllNetImgsWithLocalCache();  
 * 清除某一张图片的本地缓存:MobileFrame.ImageUtil.ImageLoaderFactory.clearNetUrlImgCache(src);  
 * 显示某一张图片:MobileFrame.ImageUtil.ImageLoaderFactory.setImgWidthLocalCache(dom,src);  
 * 图片本地缓存模块完毕********************************  
 */  
(function(global) {  
	/**  
	 * 定义全局函数对象 define出来的  
	 */  
	var mapping = {};  
	/**  
	 * 缓存,正在用到的对象,函数中return 出来的,这样就不需要重复执行函数  
	 */  
	var cache = {};  
	/**  
	 * @description 模块定义  
	 * @param {String} id id  
	 * @param {Function} func 对应的函数对象  
	 */  
	global.define = function(id, func) {  
		mapping[id] = func;  
	};  
	/**  
	 * @description 生成模块对象,并采用本地缓存  
	 * @param {String} id  
	 */  
	global.require = function(id) {  
		if (!/\.js$/.test(id)) {  
			id += '.js';  
		}  
		if (cache[id]) {  
			return cache[id];  
		} else {  
			return cache[id] = mapping[id]({});  
		}  
	};  
	/**  
	 * @description 配置全局工具类以及一些需要用到的全局函数  
	 */  
	(function() {  
		global.MobileFrame = {};  
		/**  
		 * 空函数  
		 */  
		MobileFrame.noop = function() {};  
		/**  
		 * @description each遍历操作  
		 * @param {type} elements  
		 * @param {type} callback  
		 * @returns {global}  
		 */  
		MobileFrame.each = function(elements, callback, hasOwnProperty) {  
			if (!elements) {  
				return this;  
			}  
			if (typeof elements.length === 'number') {  
				[].every.call(elements, function(el, idx) {  
					return callback.call(el, idx, el) !== false;  
				});  
			} else {  
				for (var key in elements) {  
					if (hasOwnProperty) {  
						if (elements.hasOwnProperty(key)) {  
							if (callback.call(elements[key], key, elements[key]) === false) return elements;  
						}  
					} else {  
						if (callback.call(elements[key], key, elements[key]) === false) return elements;  
					}  
				}  
			}  
			return global;  
		};  
		/**  
		 * @description plusReady  
		 * @param {Function} callback  
		 * @returns {global} 返回的是global  
		 */  
		MobileFrame.plusReady = function(callback) {  
			if (window.plus) {  
				setTimeout(function() { //解决callback与plusready事件的执行时机问题(典型案例:showWaiting,closeWaiting)  
					callback();  
				}, 0);  
			} else {  
				document.addEventListener("plusready", function() {  
					callback();  
				}, false);  
			}  
			return global;  
		};  
		/**  
		 * @description 得到相对路径对应的key,这个key可以使缓存池的或者是本地缓存键值  
		 * 主要作用是去除非法字符  
		 * @param {String} relativePath  
		 */  
		MobileFrame.getRelativePathKey = function(relativePath) {  
			var finalKey =  
				//					relativePath.replace('\/', '').replace('.', '').replace('\/', '')  
				//					.replace('_download', '').replace('jpg', '');  
				relativePath.replace(/[&\|\\\*^%$#@\-]/g, "");  
			return finalKey;  
		};  
		/**  
		 * @description 更改url类型,去除cache,因为cache会造成一些困扰  
		 * @param {String} url 传入的url  
		 */  
		MobileFrame.changImgUrlTypeNoCache = function(url) {  
			url = url || '';  
			if (url.indexOf('?') != -1) {  
				url += '&timeRandKey=' + Math.random();  
			} else {  
				url += '?timeRandKey=' + Math.random();  
			}  
			return url;  
		};  
		/**  
		 * @description 删除指定路径的文件  
		 * @param {String} relativePath  绝对路径或相对路径例如:  _downloads/imgs/test.jpg  
		 * @param {Function} successCallback  删除成功回调  
		 * @param {Function} errorCallback  失败回调  
		 */  
		MobileFrame.delFile = function(relativePath, successCallback, errorCallback) {  
			if (!relativePath) {  
				return;  
			}  
			MobileFrame.plusReady(function() {  
				plus.io.resolveLocalFileSystemURL(relativePath, function(entry) {  
					entry.remove(function(entry) {  
						if (successCallback && typeof(successCallback) == 'function') {  
							successCallback(true);  
						}  
					}, function(e) {  
						if (errorCallback && typeof(errorCallback) == 'function') {  
							errorCallback('删除文件失败!');  
						}  
					});  
				}, function() {  
					if (errorCallback && typeof(errorCallback) == 'function') {  
						errorCallback('打开文件路径失败!');  
					}  
				});  
			});  
		};  
		/**  
		 * @description 判断网络状态  
		 */  
		function GetNetWorkState() {  
			var NetStateStr = '未知';  
			var types = {};  
			types[plus.networkinfo.CONNECTION_UNKNOW] = "未知";  
			types[plus.networkinfo.CONNECTION_NONE] = "未连接网络";  
			types[plus.networkinfo.CONNECTION_ETHERNET] = "有线网络";  
			types[plus.networkinfo.CONNECTION_WIFI] = "WiFi网络";  
			types[plus.networkinfo.CONNECTION_CELL2G] = "2G蜂窝网络";  
			types[plus.networkinfo.CONNECTION_CELL3G] = "3G蜂窝网络";  
			types[plus.networkinfo.CONNECTION_CELL4G] = "4G蜂窝网络";  
			NetStateStr = types[plus.networkinfo.getCurrentType()];  
  
			return NetStateStr;  
		};  
		/**  
		 * @description 判断是否有网络  
		 */  
		MobileFrame.IsNetWorkCanUse = function() {  
			var IsCanUseNetWork = false;  
			if (GetNetWorkState() == '未知' || GetNetWorkState() == '未连接网络') {  
				IsCanUseNetWork = false;  
			} else {  
				IsCanUseNetWork = true;  
			}  
			return IsCanUseNetWork;  
		};  
	})();  
	/**  
	 * @description 定义模块功能-图片工具类  
	 */  
	define('scripts/Core/ImageUtil.js', function(exports) {  
		/**  
		 * @description 图片加载工厂,包含图片加载方法  
		 * 例如:时间戳控制缓存,下载队列批次下载,默认图片,下载loading图片,下载失败图片  
		 * 注意相对路径在Android:/sdcard/Android/data/io.dcloud.HBuilder/.HBuilder/...  
		 * iOS:/Library/Pandora/...  
		 */  
		(function(ImgLoaderFactory) {  
			/**  
			 * 默认的options  
			 */  
			var defaultSettingOptions = {  
				//默认的图片缓存目录-存到应用的downloads/imgs下  
				'imgStoragePath': "_downloads/imgs/",  
				//默认图片的基座路径  
				'defaultImgBase': '../../img/mobileFrame/',  
				//loading图片的名称  
				'loadingImgName': 'img_loading.jpg',  
				//error图片名称  
				'errorImgName': 'img_error.jpg',  
				//图片缓存的时间戳,毫秒单位,默认为1天  
				'imgsTimeStamp': 1000 * 60 * 60 * 24 * 1,  
				//同时最多的downloader 并发下载数目,默认为3个  
				'concurrentDownloadCount': 3,  
				//单个下载任务最大的请求时间,防止一些机型上无法触发错误回调,单位毫秒,默认10秒  
				'maxTimeSingleDownloadTaskSpend': 1000 * 10  
			};  
			//默认的下载图片临时变量  
			var defaultLoadingImg = defaultSettingOptions['defaultImgBase'] + defaultSettingOptions['loadingImgName'];  
			//默认的显示图片临时变量  
			var defaultImg = defaultSettingOptions['defaultImgBase'] + defaultSettingOptions['errorImgName'];  
			/**  
			 * 图片缓存的session头部  
			 */  
			var imageSessionKey_header = 'imageSessionKey_util_imgs_';  
			/**  
			 * 图片缓存的session的管理者  
			 */  
			var imageSessionManagerKey = 'imageSessionKey_util_Manager';  
			/**  
			 * 图片缓存池,用来解决多张图片并发请求问题  
			 * 默认是空的,当有多张图片是同一个请求时,缓存池子中会有数据  
			 * 格式  {'url1':[dom1,dom2]}  
			 */  
			var requestImgsPool = {};  
			/**  
			 * 并发下载任务,包括下载队列,处理最大并发数下载  
			 */  
			var concurrentDownloadTask = {  
				//任务池-还没有下载的任务  
				Queue: [],  
				//当前正在下载的任务数量  
				CurrentTaskCount: 0  
			};  
			/**  
			 * 当前的任务队列,包含任务的名称,以及时间戳-用来控制最大的超时时间,防止不能正常触发回调  
			 * 包含:  
			 * taskObj,timeBegin  
			 * 格式:{url1:{task1,time1}}  
			 */  
			var currentDownloadTasks = {};  
			/**  
			 * @description 将对应的图片缓存键值添加进入图片缓存管理中  
			 * @param {String} key 图片路径对应的key  
			 */  
			function addImageSessionKeyToManager(key) {  
				//获取管理者  
				var manager = plus.storage.getItem(imageSessionManagerKey);  
				if (manager == null) {  
					//如果以前的缓存为空,生成缓存  
					manager = [];  
				} else {  
					try {  
						manager = JSON.parse(manager);  
					} catch (e) {}  
				}  
				if (manager.indexOf(key) == -1) {  
					manager.push(key);  
				}  
				plus.storage.setItem(imageSessionManagerKey, JSON.stringify(manager));  
			};  
			/**  
			 * @description 从缓存管理中移除相应的图片缓存  
			 * @param {String} key 图片路径对应的key  
			 */  
			function removeImageSessionKeyFromManager(key) {  
				//获取管理者  
				var manager = plus.storage.getItem(imageSessionManagerKey);  
				if (manager == null) {  
					//这时候肯定没有离线缓存  
					return;  
				}  
				try {  
					manager = JSON.parse(manager);  
				} catch (e) {}  
				var index = -1;  
				for (var i = 0; i < manager.length || 0; i++) {  
					if (manager[i] == key) {  
						index = i;  
					}  
				}  
				if (index != -1) {  
					//删除对应的index位置  
					manager.splice(index, 1);  
					//重新存储  
					plus.storage.setItem(imageSessionManagerKey, JSON.stringify(manager));  
				}  
			};  
  
			/**  
			 * 设置图片缓存key  
			 * @param {String} url  
			 * @param {JSON} value 存进去的是图片相关的所有属性,包括时间戳,本地路径等  
			 */  
			function setImageSessionItem(url, value) {  
				if (url == null) {  
					return;  
				}  
				//然后添加进入缓存管理者中  
				addImageSessionKeyToManager(url);  
				url = imageSessionKey_header + MobileFrame.getRelativePathKey(url);  
				value = (value != null) ? value : '';  
				value = (typeof(value) == 'string') ? value : JSON.stringify(value);  
				plus.storage.setItem(url, value);  
  
			};  
			/**  
			 * 获取图片缓存key  
			 * @param {String} url  
			 * @return {JSON} item 返回的是一个json对象,包括图片相关的所有属性,包括时间戳,本地路径等  
			 * @example 包含属性:time localPath  
			 */  
			function getImageSessionItem(url) {  
				if (url == null) {  
					return null;  
				}  
				//去除非法字符  
				url = imageSessionKey_header + MobileFrame.getRelativePathKey(url);  
				var item = plus.storage.getItem(url);  
				try {  
					if (item != null) {  
						item = JSON.parse(item);  
					}  
				} catch (e) {}  
				return item;  
			};  
			/**  
			 * 移除图片缓存key  
			 * @param {String} url  
			 */  
			function removeImageSessionItem(url) {  
				if (url == null) {  
					return null;  
				}  
				removeImageSessionKeyFromManager(url);  
				//去除非法字符  
				url = imageSessionKey_header + MobileFrame.getRelativePathKey(url);  
				var items = plus.storage.removeItem(url);  
			};  
			/**  
			 * @description 设置图片的src地址,根据一个url,为所有需要的图片赋值  
			 * @param {HTMLElement} $img 图片的dom,这里为原生dom对象  
			 * @param {String} srcUrl 图片的路径  
			 * @param {String} relativePath 相对路径,用来判断缓存池的  
			 */  
			function setImgSrc($img, srcUrl, relativePath) {  
				if (!srcUrl) {  
					return;  
				}  
				setImgSrcByDom($img, srcUrl);  
				//如果缓存池子中存在图片  
				if (!relativePath) {  
					return;  
				}  
				var relativePathKey = MobileFrame.getRelativePathKey(relativePath);  
				if (requestImgsPool && requestImgsPool[relativePathKey]) {  
					var imgsData = requestImgsPool[relativePathKey];  
					//如果是数组  
					if (Array.isArray(imgsData)) {  
						for (var i = 0; i < imgsData.length; i++) {  
							setImgSrcByDom(imgsData[i], srcUrl);  
						}  
					} else {  
						//单条数据--单个dom对象  
						setImgSrcByDom(imgsData, srcUrl);  
					}  
					if (srcUrl != defaultLoadingImg) {  
						//如果不是loading图片就清空  
						//清空图片池子中的该条键值  
						requestImgsPool[relativePathKey] = null;  
					}  
				}  
			};  
			/**  
			 * @description 设置图片的src地址,一个dom和一个 src一一对应  
			 * @param {HTMLElement} $img 图片的dom,原生dom对象  
			 * @param {String} srcUrl 图片的路径  
			 */  
			function setImgSrcByDom($img, srcUrl) {  
				if (!$img || !($img instanceof HTMLElement)) {  
					//console.log('该dom不是原生对象,url:' + srcUrl);  
					return;  
				}  
				//暂时去除loading图片的独特样式  
//				if (srcUrl == defaultLoadingImg) {  
//					//默认的loading图片,修改css  
//					$img.style.maxWidth = '30px';  
//					$img.style.maxHeight = '30px';  
//				} else {  
//					//恢复普通高度  
//					$img.style.maxWidth = '100%';  
//					$img.style.maxHeight = '100%';  
//				}  
				srcUrl = MobileFrame.changImgUrlTypeNoCache(srcUrl);  
				$img.setAttribute('src', srcUrl);  
			};  
			/**  
			 * @description 移除所有的图片缓存键  
			 */  
			function clearAllImageSessionKey() {  
				MobileFrame.plusReady(function() {  
					var manager = plus.storage.getItem(imageSessionManagerKey);  
					if (manager == null) {  
						//这时候肯定没有离线缓存  
						return;  
					}  
					try {  
						manager = JSON.parse(manager);  
					} catch (e) {}  
					if (Array.isArray(manager)) {  
						for (var i = 0; i < manager.length; i++) {  
							removeImageSessionItem(manager[i]);  
						}  
					}  
				});  
  
			};  
			/**  
			 * @description 设置图片加载工具的一些基本参数  
			 * @param {JSON} options 参数  
			 * @example 参数没传代表使用默认值,包括:  
			 * imgStoragePath,string型,图片的默认路径  
			 * defaultImgBase,string型,默认图片的基座路径  
			 */  
			ImgLoaderFactory.setOptions = function(options) {  
				if (!options) {  
					return;  
				}  
				//设置参数  
				for (var key in defaultSettingOptions) {  
					//如果设置的是有效的  
					if (options[key] != null) {  
						defaultSettingOptions[key] = options[key];  
					}  
				}  
				//默认的下载图片临时变量  
				defaultLoadingImg = defaultSettingOptions['defaultImgBase'] + defaultSettingOptions['loadingImgName'];  
				//默认的显示图片临时变量  
				defaultImg = defaultSettingOptions['defaultImgBase'] + defaultSettingOptions['errorImgName'];  
			};  
			/**  
			 * @description 清除图片加载工厂的所有图片缓存  
			 * @param {Function} successCallback 成功回调  
			 * @param {Function} errorCallback 失败回调  
			 */  
			ImgLoaderFactory.clearLoaderImgsCache = function(successCallback, errorCallback) {  
				MobileFrame.plusReady(function() {  
					//遍历目录文件夹下的所有文件,然后删除  
					var tmpUrl = plus.io.convertLocalFileSystemURL(defaultSettingOptions['imgStoragePath']);  
					//需要手动加上 file://  
					tmpUrl = 'file://' + tmpUrl;  
					//同时清除所有的缓存键值  
					clearAllImageSessionKey();  
					plus.io.resolveLocalFileSystemURL(tmpUrl, function(entry) {  
						entry.removeRecursively(function() {  
							if (successCallback && typeof(successCallback) == 'function') {  
								successCallback('清除图片缓存成功!');  
							}  
						}, function() {  
							if (errorCallback && typeof(errorCallback) == 'function') {  
								errorCallback('清除图片缓存失败!');  
							}  
						});  
					}, function(e) {  
						if (errorCallback && typeof(errorCallback) == 'function') {  
							errorCallback('打开图片缓存目录失败!');  
						}  
					});  
				});  
			};  
			/**  
			 * @description 从一个网络URL中,获取本地图片缓存相对路径  
			 * @param {String} loadUrl 图片的网络路径,如果为null,则返回一个null  
			 * @return {String} 返回路径或者是 ***outOfTime***表示缓存时间戳过去  
			 * @example 获取相对路径可以有很多种方法  
			 * 比如可以用md5将url加密,或者其它字符串操作等等  
			 * 我这里也是根据项目而进行自适应的  
			 */  
			ImgLoaderFactory.getRelativePathFromLoadUrl = function(loadUrl) {  
				if (loadUrl == null) return null;  
				//如果loadUrl以../开头,代表是相对路径,采用本地相对路径,这里就直接返回本地路径,这样就是直接赋值了  
				if (loadUrl.substring(0, 5).indexOf('../') != -1) {  
					return loadUrl;  
				}  
				//图片缓存如果存在,判断是否过期,默认为''  
				var isOutOfTimeHeader = '';  
				//如果存在本地缓存,并且没有过期,采用本地缓存中的图片  
				var loacalImgSessionItem = getImageSessionItem(loadUrl);  
				if (loacalImgSessionItem != null) {  
					//判断是否过期  time localPath  
					if (loacalImgSessionItem.time) {  
						loacalImgSessionItem.time = parseInt(loacalImgSessionItem.time, 10);  
						if ((new Date()).valueOf() - loacalImgSessionItem.time > defaultSettingOptions['imgsTimeStamp']) {  
							//console.log('当前缓存已经过期')  
							//返回一个特殊字符,代表过期	  
							isOutOfTimeHeader = '***outOfTime***';  
						} else {  
							//console.log('缓存未过期');  
						}  
					}  
					if (loacalImgSessionItem.localPath) {  
						return loacalImgSessionItem.localPath;  
					}  
				}  
				//获取图片后缀,如果没有获取到后缀,默认是jpg  
				var imgSuffix = loadUrl.substring(loadUrl.lastIndexOf(".") + 1, loadUrl.length);  
				if (  
					imgSuffix.toLocaleLowerCase() != ("jpg") &&  
					imgSuffix.toLocaleLowerCase() != ("jpeg") &&  
					imgSuffix.toLocaleLowerCase() != ("png") &&  
					imgSuffix.toLocaleLowerCase() != ("bmp") &&  
					imgSuffix.toLocaleLowerCase() != ("svg") &&  
					imgSuffix.toLocaleLowerCase() != ("gif")  
				) {  
					//如果后缀没有包含以上图片,将后缀改为jpg  
					imgSuffix = 'jpg';  
				}  
				//更换存储方式,变为将整个路径存储下来,然后去除非法字符  
				var regIllegal = /[&\|\\\*^%$#@\-:.?\/=!]/g;  
				//获取图片名字  
				var imgName = loadUrl.replace(regIllegal, '');  
				//最终的名字  
				var filename = imgName + '.' + imgSuffix;  
				//console.log('loadurl:'+loadUrl+',fileName:'+filename);  
				var relativePath = defaultSettingOptions['imgStoragePath'] + filename;  
				setImageSessionItem(loadUrl, {  
					'localPath': relativePath,  
					'time': (new Date()).valueOf()  
				});  
				//将是否过期标识传出  
				return isOutOfTimeHeader + relativePath;  
			};  
  
			/**  
			 * @description 删除某一张网络图片的本地缓存,同时也会删除缓存键值  
			 */  
			ImgLoaderFactory.clearNetUrlImgCache = function(netImgUrl, successCallback, errorCallback) {  
				MobileFrame.plusReady(function() {  
					//删除该键值对应的缓存  
					removeImageSessionItem(netImgUrl);  
					MobileFrame.delFile(ImgLoaderFactory.getRelativePathFromLoadUrl(netImgUrl), successCallback, errorCallback);  
				});  
			};  
  
			/**  
			 * @description 给指定的图片dom 设置本地图片属性  
			 * @param {HTMLElement} $img 目标图片dom,这里为原生的dom对象  
			 * @param {String} relativePath 本地图片路径  
			 */  
			function setImgFromLocalCache($img, relativePath) {  
				/*例如:  
				 * 本地相对路径("downloads/imgs/logo.jpg")转成SD卡绝对路径  
				 * 例如相对路径:downloads/imgs/logo.jpg  
				 * ("/storage/emulated/0/Android/data/io.dcloud.HBuilder/.HBuilder/downloads/imgs/logo.jpg");  
				 * */  
				if (!relativePath) {  
					return;  
				}  
				MobileFrame.plusReady(function() {  
					var sd_path = plus.io.convertLocalFileSystemURL(relativePath);  
					setImgSrc($img, sd_path, relativePath);  
				});  
			};  
			/**  
			 * @description 通过本地缓存的方法显示网络图片  
			 * @param {HTMLElement} $img 原生dom对象  
			 * @param {String} loadUrl loadurl  
			 */  
			ImgLoaderFactory.setImgWidthLocalCache = function($img, loadUrl) {  
				if ($img == null || loadUrl == null) return;  
				MobileFrame.plusReady(function() {  
					var relativePath = ImgLoaderFactory.getRelativePathFromLoadUrl(loadUrl);  
					//判断需不需要将路径进行编码,如果是中文路径,需要编码后才能下载  
					var regChinese = /[\u4E00-\u9FA5]/g;  
					var tmpLoadUrl = loadUrl.replace(regChinese, 'chineseRemoveAfter');  
					if (tmpLoadUrl.indexOf('chineseRemoveAfter') != -1) {  
						loadUrl = encodeURI(loadUrl);  
					}  
					//判断是否已经缓存过期  
					var isCacheOutOfTime = false;  
					if (relativePath.indexOf('***outOfTime***') != -1) {  
						relativePath.replace('***outOfTime***', '');  
						isCacheOutOfTime = true;  
					}  
					if (relativePath == 'default.jpg') {  
						//设置默认图片  
						setImgSrc($img, defaultImg);  
					} else {  
						if (isCacheOutOfTime == false) {  
							//检查图片是否已存在  
							plus.io.resolveLocalFileSystemURL(relativePath, function(entry) {  
								//如果文件存在,则直接设置本地图片  
								setImgFromLocalCache($img, relativePath);  
							}, function(e) {  
								readyToGetNetImg($img, loadUrl, relativePath);  
							});  
						} else {  
							//否则,本地缓存已经过期,直接网络获取  
							readyToGetNetImg($img, loadUrl, relativePath);  
						}  
					}  
				});  
			};  
			/**  
			 * @description 准备通过网络获取图片  
			 * @param {HTMLElement} $img 原生dom对象  
			 * @param {String} loadUrl loadurl  
			 * @param {String} relativePath 本地相对路径  
			 */  
			function readyToGetNetImg($img, loadUrl, relativePath) {  
				//如果文件不存在,上网下载  
				if (MobileFrame.IsNetWorkCanUse() == true) {  
					//添加进入图片缓存池中  
					var relativePathKey = MobileFrame.getRelativePathKey(relativePath);  
					if (requestImgsPool && requestImgsPool[relativePathKey] && Array.isArray(requestImgsPool[relativePathKey])) {  
						//如果已经存在该条图片缓存池,代表这条资源已经进行请求了,只需要填进响应池子即可  
						//console.log('已经存在缓存池:'+loadUrl);  
						requestImgsPool[relativePathKey].push($img);  
						//设置一张加载中图片,这只是假象,其实并没有下载  
						setImgSrc($img, defaultLoadingImg);  
						return;  
					} else {  
						//新建缓存池  
						//console.log('新建缓存池:'+loadUrl);  
						requestImgsPool[relativePathKey] = [];  
						requestImgsPool[relativePathKey].push($img);  
					}  
					//如果网络状态能用,联网下载图片  
					setImgFromNet($img, loadUrl, relativePath);  
				} else {  
					//采用本地默认图片  
					setImgSrc($img, defaultImg);  
				}  
			};  
			/**  
			 * @description 从网络下载图片,并给指定的img dom赋值  
			 * @param {HTMLElement} $img 目标img 原生dom  
			 * @param {String} loadUrl 网络图片路径  
			 * @param {String} relativePath 图片下载后的本地路径,如果不指定,采用默认值 在_downloads/imgs/下  
			 */  
			function setImgFromNet($img, loadUrl, relativePath) {  
				relativePath = (typeof(relativePath) == 'string' && relativePath != '') ? relativePath : ImgLoaderFactory.getRelativePathFromLoadUrl(loadUrl);  
				//下载参数  
				var options = {  
					filename: relativePath,  
					timeout: 3,  
					retryInterval: 3  
				};  
				//解决ios的网络缓存问题  
				loadUrl = MobileFrame.changImgUrlTypeNoCache(loadUrl);  
				//1.将图片 设为默认的下载图片  
				setImgSrc($img, defaultLoadingImg);  
				//2.创建下载任务  
				var dtask = plus.downloader.createDownload(loadUrl,  
					options,  
					function(d, status) {  
						if (status == 200) {  
							//下载成功  
							//console.log('绝对路径:'+d.filename);  
							//这里传入的是相对路径,方便缓存显示  
							setImgFromLocalCache($img, relativePath);  
						} else {  
							//下载失败,需删除本地临时文件,否则下次进来时会检查到图片已存在  
							//console.log("下载失败=" + status + "==" + relativePath);  
							//dtask.abort();//文档描述:取消下载,删除临时文件;(但经测试临时文件没有删除,故使用delFile()方法删除);  
							if (relativePath != null) {  
								MobileFrame.delFile(relativePath);  
							}  
							setImgSrc($img, defaultImg);  
						}  
						//下载完成,当前任务数-1,并重新检查下载队列  
						concurrentDownloadTask['CurrentTaskCount']--;  
						//下载完成,从当前下载队列中去除  
						currentDownloadTasks[dtask.url] = null;  
						executeDownloadTasks();  
					});  
				//3.启动下载任务,添加进入下载队列中  
				concurrentDownloadTask['Queue'].push(dtask);  
				//执行并发下载队列  
				executeDownloadTasks();  
			};  
			/**  
			 * @description 执行下载任务,通过队列中一个一个的进行  
			 */  
			function executeDownloadTasks() {  
				//console.log('检查下载队列');  
				//先检查是否存在任务超时的  
				for (var taskItem in currentDownloadTasks) {  
					if (currentDownloadTasks[taskItem] &&  
						currentDownloadTasks[taskItem].timeBegin && (new Date()).valueOf() - currentDownloadTasks[taskItem].timeBegin > defaultSettingOptions['maxTimeSingleDownloadTaskSpend']) {  
						//如果当前下载人已经超时,并且没有自动触发回调  
						//终止任务下载  
						currentDownloadTasks[taskItem].taskObj && currentDownloadTasks[taskItem].taskObj.abort&&currentDownloadTasks[taskItem].taskObj.abort();  
						concurrentDownloadTask['CurrentTaskCount']--;  
						//从当前任务队列中去除  
						currentDownloadTasks[taskItem] = null;  
						//console.log('存在超时的任务,手动剔除');  
					}  
				}  
				//如果当前下载任务小于并发下载数		  
				if (concurrentDownloadTask['CurrentTaskCount'] < defaultSettingOptions['concurrentDownloadCount']) {  
					if (concurrentDownloadTask['Queue'].length > 0) {  
						//开启一个下载任务  
						var nowTask = concurrentDownloadTask['Queue'].shift();  
						nowTask.start()  
							//当前任务数++  
						concurrentDownloadTask['CurrentTaskCount']++;  
						currentDownloadTasks[nowTask.url] = {  
							taskObj: nowTask,  
							timeBegin: (new Date()).valueOf()  
						}  
						//console.log('添加一个下载任务');  
					}else{  
						//console.log('已经没有了下载任务');  
					}  
				}else{  
					//console.log('已经达到最大下载数量,延迟下载');  
				}  
			};  
			/**  
			 * @description 设置页面中的所有图片(本地缓存方式)  
			 * 注意,只有存在data-img-localcache 标签的图片才会有效果  
			 */  
			ImgLoaderFactory.setAllNetImgsWithLocalCache = function() {  
				//获取页面中所有的图片  
				var imgs = document.querySelectorAll('img');  
				MobileFrame.each(imgs, function(key, value) {  
					var src = this.getAttribute('data-img-localcache');  
					//console.log('显示图片:' + src);  
					if (src != null && src != '') {  
						ImgLoaderFactory.setImgWidthLocalCache(this, src);  
					}  
				});  
			};  
		})(exports.ImageLoaderFactory = {});  
  
		return exports;  
	});  
	/**  
	 * 生成模块  
	 */  
	{  
		MobileFrame.ImageUtil = require('scripts/Core/ImageUtil.js');  
	}  
  
})(this);


为了方便,就直接将源码都并入了自己写的框架类中了,
另外感谢@wenju 提供的思路!
欢迎大家留言,讨论!

源码见附件

18 关注 分享
老贪嗔痴 wenju gadget2k 不知道 未闻丶 guojiahsuai 5***@qq.com 5***@qq.com 3***@qq.com 7***@qq.com 老火 jwenlee 1***@qq.com DCloud_heavensoft orchie cydida 启程588 d***@yunfanda.com

要回复文章请先登录注册

d***@yunfanda.com

d***@yunfanda.com

收藏一下
2020-10-19 17:40
l***@live.com

l***@live.com

mark
2019-06-02 11:09
s***@163.COM

s***@163.COM

mark
2019-01-12 11:47
撒网要见鱼

撒网要见鱼 (作者)

回复 orchie :
这是很老很老的`类似于`require.js等模块化的写法,可以无需理会
2017-10-09 20:02
orchie

orchie

MobileFrame.ImageUtil = require('scripts/Core/ImageUtil.js');这里的'scripts/Core/ImageUtil.js'只要与上面define定义的一致,是否可以随便写?前提是require函数去掉.js正则判断。因为不知道写成这样有什么含义,项目中也不存在这个路径 与文件。求解惑
2017-09-29 17:42
l***@163.com

l***@163.com

mark
2017-09-25 22:18
7***@qq.com

7***@qq.com

收藏,首先感谢作者发布了这么好的文章

其次我发现个小问题,服务器返回的给我是中文路径,方法内部把首次下载的文件重新命名了,第二次判断缓存是否存在的时候,重命名方式不对,我排查在在下载文件方法内,判断缓存是否过期哪里的编码后URL和缓存内的不一致
我调试后,在判断文件是否存在那里又一次增加的编码判断,希望作者修正下
//判断缓存是否过期
if (IsLocalCacheOutOfTime(loadUrl) == false && IsUseCache == true) {
//如果缓存没有过期,并且使用了缓存
//检查文件是否已存在,如果存在就采取本地文件,否则重新获取
//console.log("判断文件是否存在:"+relativePath);
relativePath=getRelativePathFromLoadUrl(loadUrl);
plus.io.resolveLocalFileSystemURL(relativePath, function(entry) {
//如果文件存在,则直接回调本地路径
callbackOptions.successDownload && callbackOptions.successDownload(relativePath, true);
}, function(e) {
readyToDownloadFromNet(loadUrl, callbackOptions);
});
} else {
//如果没有使用缓存或者缓存已经过期,从网络获取
readyToDownloadFromNet(loadUrl, callbackOptions);
}


增加一行代码
relativePath=getRelativePathFromLoadUrl(loadUrl);
2017-05-15 14:31
撒网要见鱼

撒网要见鱼 (作者)

回复 5***@qq.com :
H5+里是基于webview运行的。无法在自己创建的后台服务里运行。
所以H5+里的下载是单独挂在在页面的, 你可以将下载放到一个不会被销毁的页面,
这样在Android中仍然会下载,但是iOS中好像是会暂停的。
2016-06-28 16:13
5***@qq.com

5***@qq.com

请问下载可以再后台运行吗? 比如下载期间依然可以使用APP的其他功能。十分感谢
2016-06-25 11:02
5***@qq.com

5***@qq.com

大神,请收下我的膝盖!!!
2016-06-08 10:56