0

最近,当我在 iOS 6.x 设备上以 web-app (fullscreen) mode运行时,我发现了一个与我的 GWT 应用程序中的缓存有关的问题。问题是iOS 似乎忽略了我对庞大排列文件( <hash>.cache.html.)的缓存策略指令

我有一个 servlet 过滤器在静态资源(包括*.cache.html文件)上设置缓存标头,例如:

# Response header snippet
Expires: Fri, 26 Jul 2013 09:58:28 GMT
Cache-Control: public, max-age=8640000
ETag: W/"322107-1359629388000"
Last-Modified: Thu, 31 Jan 2013 10:49:48 GMT

但是,一旦我加入网络应用程序支持并将应用程序添加到我的主屏幕,iOS 设备将在每次加载时请求排列文件,并且既不发送If-None-Match也不发送If-Modified-Since请求标头。通过<meta>标签添加 Web 应用支持:

<meta name="apple-mobile-web-app-capable" content="yes" />

我无法在任何地方找到此问题的文档,并且不确定这是一个错误。然而,这是我所经历的缓存在我的桌面浏览器中按预期工作,我可以在其中检查接收到的标头。我无法在任何地方嗅探用户代理并根据此信息进行区分,因此所有客户端都将收到相同的标头。


我能够通过一个 HTML5 缓存清单文件“解决”眼前的问题,如本Google I/O 演讲中所述:“Google I/O 2010 - GWT Linkers target HTML5 WebWorkers & more”,其中自定义GWT Linker生成清单文件包含所有生成的排列,例如:

CACHE MANIFEST
<hash1>.cache.html
<hash2>.cache.html
...
<hashN>.cache.html

并直接在主机页面 ( <module>.html) 中添加清单:

<!doctype html>
<html manifest="[module-path]/offline.manifest">
...

这一切都很好除了所有客户端现在必须加载所有排列,即使需要一个!我的情况是 18 个排列,每个排列在 3G 或 Edge 上约 5MB :( 这真的是最好的解决方案吗?

4

1 回答 1

0

我最终做的是进一步调查,发现我可以将它添加到所有排列脚本中,而不是在主机页面中添加清单引用。清单本身可以是一个通用(实际上是空的)文件:

鼓励作者也将主页包含在清单中,但实际上引用清单的页面会自动缓存,即使没有明确提及。(来源

因此,我写了一个简单Linker的:

  1. 创建脱机清单文件
  2. manifest属性添加到<html>所有排列脚本 ( *.cache.html) 的标记中。

Linker称为ManifestLinker并且相当简单:

package com.example.linker;

// Imports

@LinkerOrder(Order.POST)
public class ManifestLinker extends AbstractLinker {
    private static final String MANIFEST_FILE = "manifest.nocache.appcache";
    private static final String HTML_FIND = "<html>";
    private static final String HTML_REPLACE = "<html manifest=\"" + MANIFEST_FILE + "\">";

    /* (non-Javadoc)
     * @see com.google.gwt.core.ext.Linker#getDescription()
     */
    @Override
    public String getDescription() {
        return "`Manifest Linker`: Adds AppCache support for static `.cache.html` resources.";
    }

    @Override
    public ArtifactSet link(TreeLogger logger, LinkerContext context, ArtifactSet artifacts) throws UnableToCompleteException {
        ArtifactSet output = new ArtifactSet(artifacts);

        output.add(buildManifest(logger));

        for (EmittedArtifact artifact : artifacts.find(EmittedArtifact.class)) {
            if (artifact.getVisibility() == Visibility.Public && artifact.getPartialPath().endsWith(".cache.html")) {
                logger.log(TreeLogger.TRACE, "Processing file: " + artifact.getPartialPath());

                String cacheHtml = Util.readStreamAsString(artifact.getContents(logger));
                if (cacheHtml.startsWith(HTML_FIND)) {
                    cacheHtml = HTML_REPLACE + cacheHtml.substring(HTML_FIND.length()); // Replace `<html>` tag.

                    output.replace(copyArtifact(logger, artifact, cacheHtml));
                }
            }
        }

        logger.log(TreeLogger.INFO, "Manifest created and linked successfully.");

        return output;
    }

    private EmittedArtifact copyArtifact(TreeLogger logger, EmittedArtifact original, String contents) throws UnableToCompleteException {
        EmittedArtifact copy = emitString(logger, contents, original.getPartialPath());
        copy.setVisibility(original.getVisibility());

        return copy;
    }

    private EmittedArtifact buildManifest(TreeLogger logger) throws UnableToCompleteException {
        StringBuilder builder = new StringBuilder();
        builder.append("CACHE MANIFEST\n")
               .append("# Generated by ")
               .append(getClass().getSimpleName())
               .append(": ")
               .append(System.currentTimeMillis())
               .append(".\n\n")
               .append("NETWORK:\n")
               .append("*\n");

        SyntheticArtifact manifest = emitString(logger, builder.toString(), MANIFEST_FILE);

        return manifest;
    }
}

在我的<module>.gwt.xml文件中,我定义并添加了链接器:

<?xml version="1.0" encoding="UTF-8"?>
<module>
    ...
    <define-linker name="manifest" class="com.example.linker.ManifestLinker" />
    <add-linker name="manifest" />
    ...

另外,我确保通过我的设置正确的内容类型web.xml

...
<mime-mapping>
    <extension>appcache</extension>
    <mime-type>text/cache-manifest</mime-type>
</mime-mapping>
...

发出manifest.nocache.appcache的也很简单:

CACHE MANIFEST
# Generated by ManifestLinker: 1366702621298.

NETWORK:
*
于 2013-04-24T09:53:00.380 回答