【问题标题】:HTML5 appcache, Get a list of cached files in the clientHTML5 appcache,获取客户端缓存文件列表
【发布时间】:2012-02-08 12:43:36
【问题描述】:

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

在这种情况下,我的清单文件将为每个用户动态加载。有没有办法可以获取已经缓存在客户端的资源列表?

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

【问题讨论】:

    标签: javascript html html5-appcache


    【解决方案1】:

    是的。您可以使用 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) {
    
        }
    };
    

    【讨论】:

    • 谢谢@mikko。我有一些类似的解决方案,但它并不那么可靠。这就是我尝试做的......如果客户端知道它已经下载的文件,那么它可以将名称发送回服务器,服务器会将任何必要的(剩余的)文件添加到清单文件中。 (我正在尝试一个一个的做,然后我可以控制下载的顺序)。
    【解决方案2】:

    我也一直在研究用于发现正在缓存的文件的解决方案,并提出了以下建议。

    .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
    

    然后我的 auth.php 文件将文件(以块的形式)返回给浏览器,但同时也记录到服务器(我使用 DB 表),并带有较早声明的 APPID。

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

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

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

    【讨论】:

      猜你喜欢
      • 2020-09-22
      • 1970-01-01
      • 2015-10-13
      • 1970-01-01
      • 1970-01-01
      • 2013-05-30
      • 2020-08-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多