HTML5 appcache, получить список кэшированных файлов в клиенте - PullRequest
3 голосов
/ 08 февраля 2012

В моем проекте я пытаюсь использовать HTML5 appcache для кэширования статических ресурсов, таких как CSS и JS, и «пользовательских» файлов, таких как изображения и видео.Когда я говорю о пользовательских изображениях / видео, я пытаюсь иметь отдельные файлы для каждого пользователя, и мне нужно также контролировать порядок загрузки файлов.

Учитывая сценарий, мой файл манифеста будет загружен динамическидля каждого пользователя.Есть ли способ, где я могу получить список ресурсов, которые уже кэшируются на стороне клиента?

Если нет, можно ли прочитать файл ".appcache" в клиенте?

Ответы [ 2 ]

4 голосов
/ 08 февраля 2012

Да.Вы можете использовать AJAX-запрос, чтобы получить файл кэша манифеста и затем прочитать его.

Однако это не гарантирует, что браузер в вопросе имеет доступные файлы.

Ниже приведен пример кода

  • Что проверяет, кэшировали ли мы приложение HTML5 или нет

  • Если мы не находимся в кэшированном состоянии, то подсчитайте загруженные ресурсы в манифесте иотобразить индикатор выполнения в соответствии с количеством записей в кэше манифеста (всего) и выполнить ручной запрос AJAX GET для всех URL-адресов, чтобы прогреть кэш.Браузер сделает это сам, но таким образом мы сможем получить некоторую информацию о ходе процесса.

  • Когда кэш находится в известном исправном состоянии, продвигаться вперед

Отказ от ответственности: не тестируется на работу с 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) {

    }
};
0 голосов
/ 27 августа 2014

Я также работал над решением для обнаружения того, какой файл кэшируется, и придумал следующее:

.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 возвращает файл (в виде кусков) в браузер, но одновременно регистрирует на сервере (я использую таблицу БД) с ранее объявленным APPID.

Таким образом, пока обнаруживается событие «progress», можно сделать AJAX-вызов для получения последней записи для APPID, которая содержит имя файла и объем отправленных данных.

Преимущество использования этогоМетод заключается в том, что он прозрачен для других методов, обращающихся к файлам в папке «.htaccess wrapped», и в моем случае также включает в себя аутентификацию.

Когда по какой-либо причине не разрешен доступ к файлу, я возвращаю «Not Authorized»заголовки.

...