最近,我发现在我的GWT应用程序中使用iOS 6.x设备在web-app(全屏)模式下运行时出现了缓存问题。问题是 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>
标记添加了网络应用支持:
<meta name="apple-mobile-web-app-capable" content="yes" />
我无法在任何地方找到此问题并且不确定它是否是一个错误。然而,是,我所经历的。缓存在我的桌面浏览器中按预期工作,我可以检查收到的标头。我无处嗅探用户代理并根据此信息进行区分,因此所有客户端都将收到相同的标题。
我能够通过此Google I/O talk: "Google I/O 2010 - GWT Linkers target HTML5 WebWorkers & more"中显示的HTML5缓存清单文件“解决”当前问题,其中自定义GWT Linker生成包含所有生成的排列的清单文件,例如:
CACHE MANIFEST
<hash1>.cache.html
<hash2>.cache.html
...
<hashN>.cache.html
并直接在主页(<module>.html
)中添加清单:
<!doctype html>
<html manifest="[module-path]/offline.manifest">
...
我的情况是18个排列,每个大约5MB,通过3G或Edge :(这真的是最好的解决方案吗?
答案 0 :(得分:0)
我最终做的是进一步调查并发现而不是在主机页面中添加清单引用,我可以将其添加到所有排列脚本。 manifest本身可以是一个通用的(实际上是空的)文件:
鼓励作者在清单中包含主页面,但实际上,即使未明确提及,也会自动缓存引用清单的页面。 (source)
因此,我写了一个简单的Linker
:
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:
*