Javascript HTML5 appcache,获取客户端中缓存文件的列表

Javascript HTML5 appcache,获取客户端中缓存文件的列表,javascript,html,html5-appcache,Javascript,Html,Html5 Appcache,在我的项目中,我尝试使用HTML5AppCache来缓存静态资源,如CSS和JS,以及“用户特定”文件,如图像和视频。当我说特定于用户的图像/视频时,我试图为每个用户创建单独的文件,并且我还需要控制文件下载的顺序 给定该场景,我的清单文件将为每个用户动态加载。是否有一种方法可以获取已缓存在客户端的资源列表 如果没有,是否可以读取客户端中的“.appcache”文件?是。您可以使用AJAX请求获取清单缓存文件,然后读取它 但是,这并不保证问题中的浏览器具有可用的文件 下面是一个示例代码 检查我们

在我的项目中,我尝试使用HTML5AppCache来缓存静态资源,如CSS和JS,以及“用户特定”文件,如图像和视频。当我说特定于用户的图像/视频时,我试图为每个用户创建单独的文件,并且我还需要控制文件下载的顺序

给定该场景,我的清单文件将为每个用户动态加载。是否有一种方法可以获取已缓存在客户端的资源列表


如果没有,是否可以读取客户端中的“.appcache”文件?

是。您可以使用AJAX请求获取清单缓存文件,然后读取它

但是,这并不保证问题中的浏览器具有可用的文件

下面是一个示例代码

  • 检查我们是否缓存了HTML5应用程序

  • 如果我们没有处于缓存状态,那么对清单中加载的资源进行计数,并根据清单缓存条目计数(总计)显示一个进度条,并对所有URL执行手动AJAX GET请求以预热缓存。浏览器将自己完成这项工作,但通过这种方式,我们可以从过程中获得一些进度信息

  • 当缓存处于已知良好状态时,向前移动

免责声明:自2010年以来未测试过工作

/**
 * HTML5 offline manifest preloader.
 * 
 * Load all manifest cached entries, so that they are immediately available during the web app execution.
 * Display some nice JQuery progress while loading.
 * 
 * @copyright 2010 mFabrik Research Oy
 * 
 * @author Mikko Ohtamaa, http://opensourcehacker.com
 */

/**
 * Preloader class constructor.
 * 
 * Manifest is retrieved via HTTP GET and parsed.
 * All cache entries are loaded using HTTP GET.
 * 
 * Local storage attribute "preloaded" is used to check whether loading needs to be performed,
 * as it is quite taxing operation.
 * 
 * To debug this code and force retrieving of all manifest URLs, add reloaded=true HTTP GET query parameter:
 * 
 * 
 * 
 * @param {Function} endCallback will be called when all offline entries are loaded
 * 
 * @param {Object} progressMonitor ProgressMonitor object for which the status of the loading is reported.
 */
function Preloader(endCallback, progressMonitor, debug) {

    if(!progressMonitor) {
        throw "progressMonitor must be defined";
    }

    this.endCallback = endCallback;
    this.progressMonitor = progressMonitor;
    this.logging = debug; // Flag to control console.log() output   
}

Preloader.prototype = { 
    /**
     * Load HTML5 manifest and parse its data
     * 
     * @param data: String, manifest file data
     * @return Array of cache entries 
     * 
     * @throw: Exception if parsing fails
     */
    parseManifest : function(data) {

        /* Declare some helper string functions 
         * 
         * http://rickyrosario.com/blog/javascript-startswith-and-endswith-implementation-for-strings/
         *
         */
        function startswith(str, prefix) {
            return str.indexOf(prefix) === 0;
        }

        var entries = [];

        var sections = ["NETWORK", "CACHE", "FALLBACK"];
        var currentSection = "CACHE";

        var lines = data.split(/\r\n|\r|\n/);
        var i;

        if(lines.length <= 1) {
            throw "Manifest does not contain text lines";
        }

        var firstLine = lines[0];
        if(!(startswith(firstLine, "CACHE MANIFEST"))) {
            throw "Invalid cache manifest header:" + firstLine;
        }

        for(i=1; i<lines.length; i++) {

            var line = lines[i];
            this.debug("Parsing line:" + line);

            // If whitespace trimmed line is empty, skip it
            line = jQuery.trim(line);
            if(line == "") {
                continue;
            }

            if(line[0] == "#") {
                // skip comment;
                continue;
            }

            // Test for a new section
            var s = 0;
            var sectionDetected = false;
            for(s=0; s<sections.length; s++) {
                var section = sections[s];
                if(startswith(line, section + ":")) {
                    currentSection = section;
                    sectionDetected = true;
                }
            }

            if(sectionDetected) {
                continue;
            }

            // Otherwise assume we can check for cached url
            if(currentSection == "CACHE") {
                entries.push(line); 
            }

        }

        return entries;
    },

    /**
     * Manifest is given as an <html> attribute.
     */
    extractManifestURL : function() {
        var url = $("html").attr("manifest");
        if(url === null) {
            alert("Preloader cannot find manifest URL from <html> tag");
            return null;
        }
        return url;
    },

    isPreloaded : function() {
        // May be null or false
        return localStorage.getItem("preloaded") == true;
    },

    setPreloaded : function(status) {
        localStorage.setItem("preloaded", status);
    },

    /**
     * Check whether we need to purge offline cache.
     * 
     */
    isForcedReload : function() {

        // http://www.netlobo.com/url_query_string_javascript.html
        function getQueryParam(name) {
          name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
          var regexS = "[\\?&]"+name+"=([^&#]*)";
          var regex = new RegExp( regexS );
          var results = regex.exec( window.location.href );
          if (results == null) {
            return "";
          } else {
            return results[1];
          }
        }

        if(getQueryParam("reload") == "true") {
            return true;
        }   

        return false;
    },

    /**
     * Do everything necessary to set-up offline application
     */
    load : function() {

        this.debug("Entering preloader");

        if (window.applicationCache) {
            this.debug("ApplicationCache status " + window.applicationCache.status);
            this.debug("Please see http://www.w3.org/TR/html5/offline.html#applicationcache");
        } else {
            this.silentError("The browser does not support HTML5 applicationCache object");
            return; 
        }

        var cold;

        if(this.isPreloaded()) {
            // We have succesfully completed preloading before
            // ...move forward

            forceReload = this.isForcedReload(); 
            if (forceReload == true) {
                applicationCache.update();
            } else {
                this.endCallback();
                return;
            }

            cold = false;
        } else {
            cold = true;
        }

        var url = this.extractManifestURL();
        if(url === null) {
            return;
        }

        this.progressMonitor.startProgress(cold);

        $.get(url, {}, jQuery.proxy(manifestLoadedCallback, this));

        function manifestLoadedCallback(data, textStatus, xhr) { 
            this.debug("Manifest retrieved");
            var text = data;
            manifestEntries = this.parseManifest(text); 
            this.debug("Parsed manifest entries:" + manifestEntries.length);
            this.populateCache(manifestEntries);
        }
    },


    /**
     * Bootstrap async loading of cache entries.
     * 
     * @param {Object} entrires
     */
    populateCache : function(entries) {
        this.manifestEntries = entries;
        this.currentEntry = 0;
        this.maxEntry = entries.length;
        this.loadNextEntry();
    },

    /**
     * Make AJAX request to next entry and update progress bar.
     * 
     */
    loadNextEntry : function() {

        if(this.currentEntry >= this.maxEntry) {
            this.setPreloaded(true);
            this.progressMonitor.endProgress();
            this.endCallback();
        }

        var entryURL = this.manifestEntries[this.currentEntry];
        this.debug("Loading entry: " + entryURL);

        function done() {
            this.currentEntry++;
            this.progressMonitor.updateProgress(this.currentEntry, this.maxEntries);
            this.loadNextEntry();   
        }

        this.debug("Preloader fetching:" + entryURL + " (" + this.currentEntry + " / " + this.maxEntry + ")");

        $.get(entryURL, {}, jQuery.proxy(done, this));
    },

    /**
     * Write to debug console
     * 
     * @param {String} msg
     */
    debug : function(msg) {
        if(this.logging) {
            console.log(msg);
        }
    },

    /**
     * Non-end user visible error message
     *
     * @param {Object} msg
     */
    silentError : function(msg) {
        console.log(msg);
    }
};

function ProgressMonitor() {

}

ProgressMonitor.prototype = {

    /**
     * Start progress bar... initialize as 0 / 0
     */
    startProgress : function(coldVirgin) {
        $("#web-app-loading-progress-monitor").show();
        if(coldVirgin) {
            $("#web-app-loading-progress-monitor .first-time").show();
        }
    },

    endProgress : function() {
    },

    updateProgress : function(currentEntry, maxEntries) {

    }
};
/**
*HTML5离线清单预加载程序。
* 
*加载所有清单缓存项,以便它们在web应用程序执行期间立即可用。
*加载时显示一些不错的JQuery进度。
* 
*@版权所有2010 mFabrik Research Oy
* 
*@作者Mikko Ohtamaa,http://opensourcehacker.com
*/
/**
*预加载类构造函数。
* 
*清单通过HTTP GET检索并解析。
*使用HTTP GET加载所有缓存项。
* 
*本地存储属性“预加载”用于检查是否需要执行加载,
*因为这是一项相当繁重的工作。
* 
*要调试此代码并强制检索所有清单URL,请添加reloaded=true HTTP GET查询参数:
* 
* 
* 
*加载所有脱机条目时,将调用@param{Function}endCallback
* 
*@param{Object}progressMonitor报告加载状态的progressMonitor对象。
*/
函数预加载程序(endCallback、progressMonitor、debug){
如果(!progressMonitor){
抛出“必须定义progressMonitor”;
}
this.endCallback=endCallback;
this.progressMonitor=progressMonitor;
this.logging=debug;//指向控制台的标志。log()输出
}
preload.prototype={
/**
*加载HTML5清单并解析其数据
* 
*@param data:String,清单文件数据
*@return缓存项数组
* 
*@throw:解析失败时异常
*/
parseManifest:函数(数据){
/*声明一些辅助字符串函数
* 
* http://rickyrosario.com/blog/javascript-startswith-and-endswith-implementation-for-strings/
*
*/
函数startswith(str,前缀){
返回str.indexOf(前缀)==0;
}
var分录=[];
var部分=[“网络”、“缓存”、“回退”];
var currentSection=“缓存”;
var lines=data.split(/\r\n |\r |\n/);
var i;

if(lines.length我也一直在研究一个解决方案,以发现哪个文件正在被缓存,并提出了以下建议

.htaccess包装器,用于将文件抓取到appcache的目录

#.htaccess
<FilesMatch "\.(mp4|mpg|MPG|m4a|wav|WAV|jpg|JPG|bmp|BMP|png|PNG|gif|GIF)$">
    SetHandler autho
</FilesMatch>
Action autho /www/restricted_access/auth.php
#.htaccess
SetHandler autho
Action autho/www/restricted_access/auth.php
然后,我的auth.php文件将文件(以块形式)返回到浏览器,但同时也使用先前声明的APPID记录到服务器(我使用一个DB表)

这样,当检测到“progress”事件时,可以进行AJAX调用来检索APPID的最后一个条目,其中包含文件名和已发送的数据量

使用此方法的优点是,它对访问“.htaccess wrapped”文件夹中文件的其他方法是透明的,在我的例子中还包括身份验证


无论出于何种原因未授权访问文件时,我都会返回“未授权”标题。

谢谢@mikko。我有类似的解决方案,但不太可靠。这就是我尝试做的……如果客户端知道它已下载的文件,那么它可以将文件名发送回服务器,服务器将在其中添加任何nec将essary(剩余)文件添加到清单文件中。(我正在尝试一个接一个地执行此操作,然后我可以控制下载的顺序)。