2012-02-08 2 views
3

내 프로젝트에서 HTML5 appcache를 사용하여 CSS 및 JS와 같은 정적 리소스와 이미지 및 비디오와 같은 "사용자 별"파일을 캐싱하려고합니다. 사용자 특정 이미지/비디오를 말할 때 각 사용자마다 별도의 파일을 만들려고하고 있으며 파일 다운로드 순서도 제어해야합니다.HTML5 appcache, 클라이언트에 캐시 된 파일 목록 가져 오기

이 시나리오에서 내 매니페스트 파일은 모든 사용자에 대해 동적으로로드됩니다. 이미 클라이언트 측에서 캐시 된 리소스 목록을 얻을 수있는 방법이 있습니까?

그렇지 않은 경우 클라이언트의 ".appcache"파일을 읽을 수 있습니까?

답변

4

예. AJAX 요청을 사용하여 매니페스트 캐시 파일을 가져온 다음 읽을 수 있습니다.

그러나이 질문에있는 브라우저에서 파일을 사용할 수 있다고 보장 할 수는 없습니다. 우리는 다음 매니페스트에 자원을로드 계산하고 진행률을 표시 캐시 된 상태가 아닌 경우 아래

우리가 캐시 된 경우 HTML5 애플리케이션 여부

  • 를 확인하는 샘플 코드

    • 입니다 bar를 사용하여 캐시를 워밍업하는 모든 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) { 
    
        } 
    }; 
    
  • +0

    감사합니다. @mikko. 나는 다소 비슷한 해결책을 가지고 있었지만 그다지 신뢰할만한 것은 아닙니다. 클라이언트가 이미 다운로드 한 파일을 알고 있으면 서버에 다시 이름을 보낼 수 있습니다. 여기서 서버는 필요한 파일 (나머지)을 매니페스트 파일에 추가합니다. (나는 하나씩 이것을하려고 노력하고있다, 그리고 나는 다운로드의 순서를 제어 할 수있다). – NGT

    0

    때문에 작동하도록 테스트하지 캐싱되고, 다음과 함께 올라와있다.

    .appcache에 파일을 가져 오는 디렉토리의 .htaccess 래퍼.

    #.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로 서버 (I는 DB 테이블을 사용)에 동시에 기록합니다.

    그런 식으로 '진행'이벤트가 감지되는 동안 AJAX 호출은 파일 이름과 전송 된 데이터의 양을 포함하는 APPID에 대한 마지막 항목을 검색 할 수 있습니다.

    이 방법을 사용하면 다른 방법으로 '.htaccess wrapped'폴더에있는 파일에 액세스 할 수 있고 내 경우에는 인증 기능을 사용할 수 있다는 장점이 있습니다.

    어떤 이유로 든 파일에 액세스 할 수있는 권한이 없으면 'Not Authorized'헤더가 반환됩니다.

    관련 문제