• 本地缓存下载文件,download的二次封装


    来源:http://ask.dcloud.net.cn/article/524

    源码下载链接 

    说明: 
    (1)由于平时项目中大量用到了附件下载等功能,所以就花了一个时间,把plus的downlaod进行了二次封装,用本地缓存方式来下载任何文件.
    (2)这个也是在前面得本地缓存下载图片的基础上完善的,拓展了下,可以下载任何文件
    功能:
    1.本地缓存下载文件,如果用本地缓存方式,在缓存有效期会优先使用本地缓存
    2. 基于plus的storage,对每一个文件的缓存进行控制管理,增加时间戳参数,来控制本地缓存的有效时间
    3. 将所有的下载任务放入队列中,进行统一下载管理,增加最大并发请求数,防止一次性请求过多损耗性能
    4. 加入了文件缓存池机制,对于同一个下载路径的文件不会多次下载,而是填入缓存池,下载后统一回调
    5. 加入了手动剔除超时任务的处理,如果存在一个任务一直没有下载完成,也没有触发回调,会在下次下载时剔除任务,并触发错误回调
    注: 这也是从自己写的框架中扒下来的,为了方便,就去除了一些无用的了。

    使用方法:详情见示例源码

     * 1.setOptions 设置下载参数
     * 2.clearAllLocalFileCache 清除所有的本地缓存,设置参数路径内的缓存
     * 3.clearNetUrlFileCache 删除某一个下载路径对应的本地缓存
     * 4.downloadFileWidthLocalCache 通过本地缓存方法下载网络文件
     * 5.RestoreOptions 还原默认的下载参数
     * 6.abortTaskByUrl 根据url,取消对应的任务
     * 7.abortAllTask 取消所有的任务

    一个示例调用例子:

    function downloadFile(IsWithCache) {
            var showProgressbar = null;
            var isCompleted = false;
            DownloadUtil.downloadFileWidthLocalCache(fileUrl, {
                beforeDownload: function() {
                    console.log('准备开始上传');
                    showProgressbar = plus.nativeUI.showWaiting('准备开始上传', {
                        back: "close",
                        padlock: true
                    });
                    showProgressbar.onclose = function() {
                        if (isCompleted == false) {
                            DownloadUtil.abortTaskByUrl(fileUrl);
                        }
                    };
                },
                successDownload: function(relativePath) {
                    isCompleted = true;
                    if (showProgressbar) {
                        showProgressbar.close();
                    }
                    console.log('下载成功:' + relativePath);
                    Zepto('#fileName').text(relativePath);
                },
                errorDownload: function(msg) {
                    isCompleted = true;
                    if (showProgressbar) {
                        showProgressbar.close();
                    }
                    Zepto('#fileName').text('下载失败:' + msg);
                },
                downloading: function(progress, tips) {
                    console.log('下载进度为:' + progress + '%,' + tips);
                    if (showProgressbar) {
                        showProgressbar.setTitle(parseInt(progress) + "%," + tips);
                    }
                }
            }, IsWithCache);
        };

    ** 源码:**

    /**
     * @description   移动开发框架
     * @author dailc  dailc 
     * @version 1.0
     * @time 2016-01-11 16:57:57
     * 功能模块:
     * 通用框架类************************************
     * scripts/Core/MobileFrame.js
     * 1.包含一个plusReady 操作
     * 2.包含一个 each操作
     * 3.IsInclude 判断是否包含文件
     * 4.IsNetWorkCanUse 判断是否有网络
     * 通用框架类完毕*********************************
     * 常用工具类****************************************
     * scripts/Core/MobileFrame_CommonUtil.js
     * 1.compareVersion 比较两个版本大小
     * 2.getRelativePathKey 得到一个path的key-这个key是去除了非法字符的,可以用来本地缓存
     * 3.changImgUrlTypeWithRandomKey 将url后面加上随机的key,用来去除缓存,否则同一个url会有缓存
     * 4.delFile 删除本地文件
     * 常用工具类完毕*************************************
     * File工具类***************************************
     * scripts/Core/MobileFrame_FileUtil.js
     * 1.delFile 删除文件
     * File工具类完毕************************************
     * 下载工具类****************************************
     * 1.本地缓存下载文件
     * 2.增加storage,增加每一个本地缓存的有效时间戳
     * 3.增加自定义设置方法,可以根据不同需求,对参数进行修改
     * 4.采用下载队列进行下载管理,增加最大并发请求数,防止一次性请求过多损耗性能
     * 注意:如果用了图片工具类.并且自定义了下载路径,下载工具类的默认参数也会相应变化,需要手动设置回来
     * 1.setOptions 设置下载参数
     * 2.clearAllLocalFileCache 清除所有的本地缓存,设置参数路径内的缓存
     * 3.clearNetUrlFileCache 删除某一个下载路径对应的本地缓存
     * 4.downloadFileWidthLocalCache 通过本地缓存方法下载网络文件
     * 5.RestoreOptions 还原默认的下载参数
     * 6.abortTaskByUrl 根据url,取消对应的任务
     * 7.abortAllTask 取消所有的任务
     * 下载工具类完毕*************************************
     */
    (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 {
                if (mapping[id]) {
                    cache[id] = mapping[id]({})
                }
                return cache[id];
            }
        };
        /**
         * @description 定义模块功能--通用框架类
         */
        define('scripts/Core/MobileFrame.js', function(exports) {
            /**
             * 空函数
             */
            exports.noop = function() {};
            /**
             * @description each遍历操作
             * @param {type} elements
             * @param {type} callback
             * @returns {global}
             */
            exports.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
             */
            exports.plusReady = function(callback) {
                if (window.plus) {
                    setTimeout(function() { //解决callback与plusready事件的执行时机问题(典型案例:showWaiting,closeWaiting)
                        callback();
                    }, 0);
                } else {
                    document.addEventListener("plusready", function() {
                        callback();
                    }, false);
                }
                return global;
            };
            /**
             * @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 判断是否有网络
             */
            exports.IsNetWorkCanUse = function() {
                var IsCanUseNetWork = false;
                if (GetNetWorkState() == '未知' || GetNetWorkState() == '未连接网络') {
                    IsCanUseNetWork = false;
                } else {
                    IsCanUseNetWork = true;
                }
                return IsCanUseNetWork;
            };
            /**
             * @description 判断是否存在js或者css
             * @param {String} name js或者css的名字
             */
            exports.IsInclude = function(name) {
                var js = /js$/i.test(name);
                var es = document.getElementsByTagName(js ? 'script' : 'link');
                for (var i = 0; i < es.length; i++)
                    if (es[i][js ? 'src' : 'href'].indexOf(name) != -1) return true;
                return false;
            };
            return exports;
        });
        //*** 通用框架类完毕
        /**
         * @description 定义模块功能-常用工具类
         */
        define('scripts/Core/MobileFrame_CommonUtil.js', function(exports) {
            /**
             * @description 比较两个版本大小
             * 比较版本大小,如果新版本nowVersion大于旧版本OldResourceVersion则返回true,否则返回false
             * @param {String} OldVersion
             * @param {String} nowVersion
             */
            exports.compareVersion = function(OldVersion, nowVersion) {
                if (!OldVersion || !nowVersion || OldVersion == '' || nowVersion == '') {
    
                    return false;
                }
                //第二份参数 是 数组的最大长度
                var OldVersionA = OldVersion.split(".", 4);
                var nowVersionA = nowVersion.split(".", 4);
                for (var i = 0; i < OldVersionA.length && i < nowVersionA.length; i++) {
                    var strOld = OldVersionA[i];
                    var numOld = parseInt(strOld);
                    var strNow = nowVersionA[i];
                    var numNow = parseInt(strNow);
                    //小版本到高版本
                    if (numNow > numOld
                        //||strNow.length>strOld.length
                    ) {
                        return true;
                    } else if (numNow < numOld) {
                        return false;
                    }
                }
                //如果是版本  如 1.6 - 1.6.1
                if (nowVersionA.length > OldVersionA.length && 0 == nowVersion.indexOf(OldVersion)) {
                    return true;
                }
            };
            /**
             * @description 得到相对路径对应的key,这个key可以使缓存池的或者是本地缓存键值
             * 主要作用是去除非法字符
             * @param {String} relativePath
             */
            exports.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
             */
            exports.changImgUrlTypeWithRandomKey = function(url) {
                url = url || '';
                if (url.indexOf('?') != -1) {
                    url += '&timeRandKey=' + Math.random();
                } else {
                    url += '?timeRandKey=' + Math.random();
                }
                return url;
            };
            return exports;
        });
        //*** 常用工具类完毕
        /**
         * @description 定义模块功能-File工具类
         */
        define('scripts/Core/MobileFrame_FileUtil.js', function(exports) {
            var MobileFrame = require('scripts/Core/MobileFrame.js');
            /**
             * @description 删除指定路径的文件
             * @param {String} relativePath  绝对路径或相对路径例如:  _downloads/imgs/test.jpg
             * @param {Function} successCallback  删除成功回调
             * @param {Function} errorCallback  失败回调
             */
            exports.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('打开文件路径失败!');
                        }
                    });
                });
            };
            return exports;
        });
        //*** File工具类完毕
        /**
         * @description 定义模块功能-下载工具类,使用本地缓存进行下载
         */
        define('scripts/Core/DownLoadUtil.js', function(exports) {
            var MobileFrame = require('scripts/Core/MobileFrame.js');
            var CommonUtil = require('scripts/Core/MobileFrame_CommonUtil.js');
            var FileUtil = require('scripts/Core/MobileFrame_FileUtil.js');
            /**
             * 默认的options
             */
            var defaultSettingOptions = {
                //默认的下载缓存目录-存到应用的downloads/downloadFiles下
                'downloadStoragePath': "_downloads/downloadFiles/",
                //本地缓存的时间戳,毫秒单位,默认为1天
                'fileCacheTimeStamp': 1000 * 60 * 60 * 24 * 1,
                //同时最多的downloader 并发下载数目,默认为3个
                'concurrentDownloadCount': 3,
                //超时请求时间
                'timeout': 3,
                //超时请求后的重试次数
                'retryInterval': 3,
                //单个下载任务最大的请求时间,防止一些机型上无法触发错误回调,单位毫秒,默认10秒
                'maxTimeSingleDownloadTaskSpend': 1000 * 10,
                //获取相对路径的函数,如果不传,则用默认的路径处理方法
                'getRelativePathFromLoadUrlCallback': null,
                //监听进度的步长
                'ListenerProgressStep':5
            };
            /**
             * 备份一个默认的设置
             */
            var oldDefaultSettingOptions = defaultSettingOptions;
            /**
             * 文件缓存的session头部
             */
            var SessionKey_header = 'downloadFile_SessionKey_util_caches_';
            /**
             * 文件缓存的session的管理者
             */
            var SessionManagerKey = 'downloadFile_SessionKey_util_Manager';
            /**
             * 文件缓存池,用来解决同一个url多次并发请求问题
             * 默认是空的,当有多个url是同一个请求时,缓存池子中会有数据
             * 格式  {'url1':[succCb1,succCb2]}
             */
            var requestUrlCachePool = {};
            /**
             * 并发下载任务,包括下载队列,处理最大并发数下载
             */
            var concurrentDownloadTask = {
                //任务池-还没有下载的任务
                Queue: [],
                //当前正在下载的任务数量
                CurrentTaskCount: 0
            };
            /**
             * 当前的任务队列,包含任务的名称,以及时间戳-用来控制最大的超时时间,防止不能正常触发回调
             * 包含:
             * taskObj,timeBegin
             * 格式:{url1:{task1,time1}}
             */
            var currentDownloadTasks = {};
            /**
             * @description 将对应的缓存键值添加进入缓存管理中
             * @param {String} key url对应的key
             */
            function addSessionKeyToManager(key) {
                //获取管理者
                var manager = plus.storage.getItem(SessionManagerKey);
                if (manager == null) {
                    //如果以前的缓存为空,生成缓存
                    manager = [];
                } else {
                    try {
                        manager = JSON.parse(manager);
                    } catch (e) {}
                }
                if (manager.indexOf(key) == -1) {
                    manager.push(key);
                }
                plus.storage.setItem(SessionManagerKey, JSON.stringify(manager));
            };
            /**
             * @description 从缓存管理中移除相应的缓存key
             * @param {String} key url对应的key
             */
            function removeSessionKeyFromManager(key) {
                //获取管理者
                var manager = plus.storage.getItem(SessionManagerKey);
                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(SessionManagerKey, JSON.stringify(manager));
                }
            };
            /**
             * 设置缓存key
             * @param {String} url
             * @param {JSON} value 存进去的是相关的所有属性,包括时间戳,本地路径等
             */
            function setSessionItem(url, value) {
                if (url == null) {
                    return;
                }
                //然后添加进入缓存管理者中
                addSessionKeyToManager(url);
                url = SessionKey_header + CommonUtil.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 getSessionItem(url) {
                if (url == null) {
                    return null;
                }
                //去除非法字符
                url = SessionKey_header + CommonUtil.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 removeSessionItem(url) {
                if (url == null) {
                    return null;
                }
                removeSessionKeyFromManager(url);
                //去除非法字符
                url = SessionKey_header + CommonUtil.getRelativePathKey(url);
                var items = plus.storage.removeItem(url);
            };
            /**
             * @description 移除所有的缓存键
             */
            function clearAllSessionKey() {
                MobileFrame.plusReady(function() {
                    var manager = plus.storage.getItem(SessionManagerKey);
                    if (manager == null) {
                        //这时候肯定没有离线缓存
                        return;
                    }
                    try {
                        manager = JSON.parse(manager);
                    } catch (e) {}
                    if (Array.isArray(manager)) {
                        for (var i = 0; i < manager.length; i++) {
                            removeSessionItem(manager[i]);
                        }
                    }
                });
            };
            /**
             * @description 设置options
             * @param {JSON} options
             */
            exports.setOptions = function(options) {
                if (!options) {
                    return;
                }
                //设置参数
                for (var key in defaultSettingOptions) {
                    //如果设置的是有效的
                    if (options[key] != null) {
                        defaultSettingOptions[key] = options[key];
                    }
                }
            };
            /**
             * @description 还原下载工具的参数,还原到默认值
             */
            exports.RestoreOptions = function() {
                if (oldDefaultSettingOptions) {
                    defaultSettingOptions = oldDefaultSettingOptions;
                }
            };
            /**
             * @description 清除下载工具的的所有本地缓存---路径为设置参数中的StoragePath
             * @param {Function} successCallback 成功回调
             * @param {Function} errorCallback 失败回调
             */
            exports.clearAllLocalFileCache = function(successCallback, errorCallback) {
                MobileFrame.plusReady(function() {
                    //遍历目录文件夹下的所有文件,然后删除
                    var tmpUrl = plus.io.convertLocalFileSystemURL(defaultSettingOptions['downloadStoragePath']);
                    //需要手动加上 file://
                    tmpUrl = 'file://' + tmpUrl;
                    //同时清除所有的缓存键值
                    clearAllSessionKey();
                    plus.io.resolveLocalFileSystemURL(tmpUrl, function(entry) {
                        entry.removeRecursively(function() {
                            if (successCallback && typeof(successCallback) == 'function') {
                                successCallback('清除本地缓存成功!路径:' + defaultSettingOptions['downloadStoragePath']);
                            }
                        }, function() {
                            if (errorCallback && typeof(errorCallback) == 'function') {
                                errorCallback('清除本地缓存失败!路径:' + defaultSettingOptions['downloadStoragePath']);
                            }
                        });
                    }, function(e) {
                        if (errorCallback && typeof(errorCallback) == 'function') {
                            errorCallback('打开本地缓存目录失败!' + defaultSettingOptions['downloadStoragePath']);
                        }
                    });
                });
            };
            /**
             * @description 删除某一个网络路径文件对应的的本地缓存,同时也会删除缓存键值
             */
            exports.clearNetUrlFileCache = function(netUrl, successCallback, errorCallback) {
                MobileFrame.plusReady(function() {
                    //删除该键值对应的缓存
                    removeSessionItem(netUrl);
                    FileUtil.delFile(getRelativePathFromLoadUrl(netUrl), successCallback, errorCallback);
                });
            };
            /**
             * @description 根据url,取消这个路径对应的下载任务
             * @param {String} loadUrl
             */
            exports.abortTaskByUrl = function(loadUrl) {
                //取消进行中任务
                currentDownloadTasks[loadUrl].taskObj && currentDownloadTasks[loadUrl].taskObj.abort && currentDownloadTasks[loadUrl].taskObj.abort();
                concurrentDownloadTask['CurrentTaskCount']--;
                //从当前任务队列中去除
                currentDownloadTasks[loadUrl] = null;
                //触发错误回调
                checkDownloadSuccessOrError(loadUrl, false);
    
                //取消队列中的任务
                //清除队列中对应id的任务
                for (var i = 0; i < concurrentDownloadTask['Queue'].length; i++) {
                    if (concurrentDownloadTask['Queue'][i].task &&
                        concurrentDownloadTask['Queue'][i].task.url == loadUrl) {
                        concurrentDownloadTask['Queue'][i].callbacks
                        &&concurrentDownloadTask['Queue'][i].callbacks.errorDownload
                        &&concurrentDownloadTask['Queue'][i].callbacks.errorDownload('下载队列中的任务被外部强行终结,url:' + loadUrl);
                        //移除对应位置的元素
                        concurrentDownloadTask['Queue'].splice(i,1);
                    }
                }
            };
            /**
             * @description 取消下载工具类中的所有下载任务
             */
            exports.abortAllTask = function() {
                //先取消进行中的任务
                for (var taskItem in currentDownloadTasks) {
                    currentDownloadTasks[taskItem].taskObj && currentDownloadTasks[taskItem].taskObj.abort && currentDownloadTasks[taskItem].taskObj.abort();
                    //从当前任务队列中去除
                    currentDownloadTasks[taskItem] = null;
                    //触发错误回调
                    checkDownloadSuccessOrError(taskItem, false);
                }
                //清除备用队列
                //取消队列中的任务
                //清除队列中所有任务
                for (var i = 0; i < concurrentDownloadTask['Queue'].length; i++) {
                    if (concurrentDownloadTask['Queue'][i].task ) {
                        concurrentDownloadTask['Queue'][i].callbacks
                        &&concurrentDownloadTask['Queue'][i].callbacks.errorDownload
                        &&concurrentDownloadTask['Queue'][i].callbacks.errorDownload('下载队列中的任务被外部强行终结,url:' + loadUrl);
                    }
                }
                concurrentDownloadTask['Queue'] = [];
                concurrentDownloadTask['CurrentTaskCount'] = 0;
            };
            /**
             * @description 路径处理方法,优先从回调函数中获取
             * @param {String} loadUrl
             */
            function getRelativePathFromLoadUrl(loadUrl) {
                var relativePath = null;
                if (defaultSettingOptions['getRelativePathFromLoadUrlCallback'] && typeof(defaultSettingOptions['getRelativePathFromLoadUrlCallback']) == 'function') {
                    //如果存在传入的回调
                    relativePath = defaultSettingOptions['getRelativePathFromLoadUrlCallback'](loadUrl);
                } else {
                    //采用默认的路径处理
                    //获取图片后缀,如果没有获取到后缀
                    var fileSuffix = loadUrl.substring(loadUrl.lastIndexOf(".") + 1, loadUrl.length);
                    fileSuffix = fileSuffix || 'file';
                    //更换存储方式,变为将整个路径存储下来,然后去除非法字符
                    var regIllegal = /[&|\*^%$#@-:.?/=!]/g;
                    //获取文件名字
                    var fileName = loadUrl.replace(regIllegal, '');
                    //最终的名字
                    var finalFileFullName = fileName + '.' + fileSuffix;
                    relativePath = defaultSettingOptions['downloadStoragePath'] + finalFileFullName;
                }
                return relativePath;
            };
            /**
             * @description 判断该下载对应的本地缓存是否过期,
             * @param {String} loadUrl
             */
            function IsLocalCacheOutOfTime(loadUrl) {
                //如果存在本地缓存,并且没有过期,采用本地缓存中的文件
                var loacalSessionItem = getSessionItem(loadUrl);
                if (loacalSessionItem != null) {
                    //判断是否过期  time localPath
                    if (loacalSessionItem.time) {
                        loacalSessionItem.time = parseInt(loacalSessionItem.time, 10);
                        if ((new Date()).valueOf() - loacalSessionItem.time > defaultSettingOptions['fileCacheTimeStamp']) {
                            //console.log('当前缓存已经过期')
                            //返回一个特殊字符,代表过期 
                            return true;
                        } else {
                            //console.log('缓存未过期');
                            return false;
                        }
                    }
                }
                return false;
            };
            /**
             * @description 通过本地缓存的方法下载文件
             * @param {String} loadUrl loadurl
             * @param {JSON} callbackOptions 存放各种回调函数
             * 包括  beforeDownload,downloading successDownload,errorDownload
             * @param {Boolean} IsUseCache 是否使用缓存,默认为true
             */
            exports.downloadFileWidthLocalCache = function(loadUrl, callbackOptions, IsUseCache) {
                if (loadUrl == null) return;
                IsUseCache = typeof(IsUseCache) == 'boolean' ? IsUseCache : true;
                callbackOptions = callbackOptions || {};
                MobileFrame.plusReady(function() {
                    var relativePath = getRelativePathFromLoadUrl(loadUrl);
                    //判断需不需要将路径进行编码,如果是中文路径,需要编码后才能下载
                    var regChinese = /[u4E00-u9FA5]/g;
                    var tmpLoadUrl = loadUrl.replace(regChinese, 'chineseRemoveAfter');
                    if (tmpLoadUrl.indexOf('chineseRemoveAfter') != -1) {
                        loadUrl = encodeURI(loadUrl);
                    }
                    //判断缓存是否过期
                    if (IsLocalCacheOutOfTime(loadUrl) == false && IsUseCache == true) {
                        //如果缓存没有过期,并且使用了缓存
                        //检查文件是否已存在,如果存在就采取本地文件,否则重新获取
                        plus.io.resolveLocalFileSystemURL(relativePath, function(entry) {
                            //如果文件存在,则直接回调本地路径
                            callbackOptions.successDownload && callbackOptions.successDownload(relativePath, true);
                        }, function(e) {
                            readyToDownloadFromNet(loadUrl, callbackOptions);
                        });
                    } else {
                        //如果没有使用缓存或者缓存已经过期,从网络获取
                        readyToDownloadFromNet(loadUrl, callbackOptions);
                    }
                });
            };
            /**
             * @description 准备通过网络下载
             * @param {String} loadUrl loadurl
             * @param {JSON} callbackOptions 存放各种回调函数
             * 包括  beforeDownload,downloading successDownload,errorDownload
             */
            function readyToDownloadFromNet(loadUrl, callbackOptions) {
                callbackOptions = callbackOptions || {};
                //如果文件不存在,上网下载
                if (MobileFrame.IsNetWorkCanUse() == true) {
                    //添加进入缓存池中
                    var relativePath = getRelativePathFromLoadUrl(loadUrl);
                    var relativePathKey = CommonUtil.getRelativePathKey(relativePath);
                    if (requestUrlCachePool && requestUrlCachePool[relativePathKey] && Array.isArray(requestUrlCachePool[relativePathKey])) {
                        //如果已经存在该条缓存池,代表这条资源已经进行请求了,只需要填进响应池子即可
                        //console.log('已经存在缓存池:'+relativePathKey);
                        requestUrlCachePool[relativePathKey].push(callbackOptions);
                        //1.下载之前的回调
                        callbackOptions.beforeDownload && callbackOptions.beforeDownload();
                        return;
                    } else {
                        //新建缓存池
                        //console.log('新建缓存池:'+relativePathKey);
                        requestUrlCachePool[relativePathKey] = [];
                        requestUrlCachePool[relativePathKey].push(callbackOptions);
                    }
                    //如果网络状态能用,联网下载
                    downloadFileFromNet(loadUrl, callbackOptions);
                } else {
                    callbackOptions.errorDownload && callbackOptions.errorDownload('下载失败:' + '没有网络!' + ',url:' + loadUrl);
                }
            };
            /**
             * @description 从网络下载文件,并通过回调函数回调
             * @param {String} loadUrl 网络路径
             * @param {JSON} callbackOptions 存放各种回调函数
             * 包括  beforeDownload,downloading successDownload,errorDownload
             */
            function downloadFileFromNet(loadUrl, callbackOptions) {
                var relativePath = getRelativePathFromLoadUrl(loadUrl);
                if (relativePath == null) {
                    return;
                }
                callbackOptions = callbackOptions || {};
                //下载参数
                var options = {
                    filename: relativePath,
                    timeout: defaultSettingOptions['timeout'],
                    retryInterval: defaultSettingOptions['retryInterval']
                };
                //存一个最原始的地址,缓存是根据最原始的地址来的
                var originalUrl = loadUrl;
                //解决ios的网络缓存问题
                loadUrl = CommonUtil.changImgUrlTypeWithRandomKey(loadUrl);
                //1.下载之前的回调
                callbackOptions.beforeDownload && callbackOptions.beforeDownload();
                //2.创建下载任务
                var dtask = plus.downloader.createDownload(loadUrl,
                    options,
                    function(d, status) {
                        if (status == 200) {
                            //下载成功
                            //console.log('绝对路径:'+d.filename);
                            //这里传入的是相对路径,方便缓存显示,回调过去的是相对路径
                            checkDownloadSuccessOrError(originalUrl, true);
                        } else {
                            //下载失败,需删除本地临时文件,否则下次进来时会检查到图片已存在
                            //console.log("下载失败=" + status + "==" + relativePath);
                            //dtask.abort();//文档描述:取消下载,删除临时文件;(但经测试临时文件没有删除,故使用delFile()方法删除);
                            if (relativePath != null) {
                                FileUtil.delFile(relativePath);
                            }
                            checkDownloadSuccessOrError(originalUrl, false);
                        }
                        //下载完成,当前任务数-1,并重新检查下载队列
                        concurrentDownloadTask['CurrentTaskCount']--;
                        //下载完成,从当前下载队列中去除
                        currentDownloadTasks[dtask.url] = null;
                        executeDownloadTasks();
                    });
                //3.添加进度监听器,监听步长也由外部传入
                var step = 0;
                var progress = 0;
                dtask.addEventListener("statechanged", function(task, status) {
                    switch (task.state) {
                        case 1: // 开始
                            callbackOptions.downloading && callbackOptions.downloading(0, "开始下载...");
                            break;
                        case 2: // 已连接到服务器
                            callbackOptions.downloading && callbackOptions.downloading(0, "已连接到服务器");
                            break;
                        case 3:
                            //每隔一定的比例显示一次
                            if (task.totalSize != 0) {
                                var progress = task.downloadedSize / task.totalSize * 100;
                                progress = Math.round(progress);
                                if (progress - step>=defaultSettingOptions.ListenerProgressStep) {
                                    step = progress;
                                    callbackOptions.downloading && callbackOptions.downloading(parseInt(progress), "下载中");
                                }
                            }
                            break;
                        case 4: // 下载完成
                            callbackOptions.downloading && callbackOptions.downloading(100, "下载完成100%");
                            break;
                    }
                });
                //4.启动下载任务,添加进入下载队列中
                concurrentDownloadTask['Queue'].push({
                    task: dtask,
                    callbacks: callbackOptions
                });
                //执行并发下载队列
                executeDownloadTasks();
            };
            /**
             * @description 某一个url下载成功后检查回调和缓存池
             * @param {String} loadUrl
             * @param {Boolean} IsSuccess
             */
            function checkDownloadSuccessOrError(loadUrl, IsSuccess) {
                var relativePath = getRelativePathFromLoadUrl(loadUrl);
                var relativePathKey = CommonUtil.getRelativePathKey(relativePath);
                if (requestUrlCachePool && requestUrlCachePool[relativePathKey]) {
                    var callbackData = requestUrlCachePool[relativePathKey];
                    //如果是数组
                    if (Array.isArray(callbackData)) {
                        for (var i = 0; i < callbackData.length; i++) {
                            if (IsSuccess == true) {
                                callbackData[i].successDownload && callbackData[i].successDownload(relativePath, IsSuccess);
                            } else {
                                callbackData[i].errorDownload && callbackData[i].errorDownload('下载失败', IsSuccess);
                            }
                        }
                    } else {
                        //单条数据--单个对调
                        if (IsSuccess == true) {
                            callbackData.successDownload && callbackData.successDownload(relativePath, IsSuccess);
                        } else {
                            callbackData.errorDownload && callbackData.errorDownload('下载失败', IsSuccess);
                        }
                    }
                    requestUrlCachePool[relativePathKey] = null;
                }
            };
            /**
             * @description 执行下载任务,通过队列中一个一个的进行
             */
            function executeDownloadTasks() {
                //console.log('检查下载队列');
                //先检查是否存在任务超时的
                //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;
                        //触发错误回调
                        checkDownloadSuccessOrError(taskItem, false);
                        //console.log('存在超时的任务,手动剔除');
                    }
                }
                //如果当前下载任务小于并发下载数       
                if (concurrentDownloadTask['CurrentTaskCount'] < defaultSettingOptions['concurrentDownloadCount']) {
                    if (concurrentDownloadTask['Queue'].length > 0) {
                        //开启一个下载任务
                        var nowTaskOptions = concurrentDownloadTask['Queue'].shift();
                        var nowTask = nowTaskOptions.task;
                        nowTask.start()
                            //当前任务数++
                        concurrentDownloadTask['CurrentTaskCount']++;
                        currentDownloadTasks[nowTask.url] = {
                                taskObj: nowTask,
                                timeBegin: (new Date()).valueOf()
                            }
                            //console.log('添加一个下载任务');
                    } else {
                        //console.log('已经没有了下载任务');
                    }
                } else {
                    //console.log('已经达到最大下载数量,延迟下载');
                }
            };
            return exports;
        });
        //*** 下载工具类完毕
    })(this);
  • 相关阅读:
    聊聊.net程序设计
    使用ftp自动下载上传文件
    Microsoft .NET Framework 2.0对文件传输协议(FTP)操作(异步上传,下载等)实现汇总2
    一个的FTP类
    网站需要提高安全性
    极速理解设计模式系列【目录索引】
    NPOI 1.2教程
    Agile Tour 2011北京站“让敏捷落地”
    Asp.net程序中用NPOI生成标准Excel报表,导入导出一应俱全[转]
    网站性能优化之HTTP请求过程
  • 原文地址:https://www.cnblogs.com/lizeyan/p/5548706.html
Copyright © 2020-2023  润新知