如何在OSGI中控制网页缓存?如何将AEM资源存入浏览器缓存?
Got it, let's break down how to control browser caching for AEM resources (images, fonts) in an OSGi environment—you're on the right track with slingResponse.setHeader(), but probably missing a few key pieces around when and how to apply those headers correctly. Here's a step-by-step solution:
核心思路先理清楚
浏览器缓存 不需要你单独指定资源路径 来触发,核心逻辑是:当浏览器请求某个资源(比如/content/dam/my-image.png)时,你的服务返回的响应里带上正确的缓存控制头,浏览器就会自动缓存该资源。你之前用setHeader()没生效,大概率是头的配置不对,或者没在正确的时机(资源请求的处理链路中)设置。
方案1:利用AEM Dispatcher做全局缓存(推荐)
如果你的AEM部署了Dispatcher(生产环境基本都有),这是最省心的全局控制方式,不需要写代码:
- 在Dispatcher配置文件(
dispatcher.any)里添加缓存规则,针对图片、字体后缀设置缓存时长:/cache { /rules { /0001 { /type "allow" /glob "*.png" /headers { /Cache-Control "public, max-age=86400" # 缓存1天 } } /0002 { /type "allow" /glob "*.jpg" /headers { /Cache-Control "public, max-age=86400" } } /0003 { /type "allow" /glob "*.woff*" /headers { /Cache-Control "public, max-age=604800" # 缓存1周 } } } } - 同时确保Dispatcher传递这些缓存头到浏览器,不要在配置里剥离响应头。
方案2:OSGi Filter拦截资源请求(代码层面细粒度控制)
如果需要针对特定路径、特定资源类型做个性化缓存控制,可以写一个OSGi Filter,拦截资源请求并设置响应头:
步骤1:创建Filter类
import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Service; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.api.resource.ResourceResolverFactory; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Set; import java.util.Map; @Component( service = Filter.class, property = { "sling.filter.scope=REQUEST", "sling.filter.pattern=/content/dam/.*", // 拦截DAM下的所有资源,可按需调整 "service.ranking=100" // 确保Filter在其他处理逻辑前执行 } ) public class ResourceCacheFilter implements Filter { private static final Set<String> CACHEABLE_EXTENSIONS = Set.of("png", "jpg", "jpeg", "woff", "woff2", "ttf", "otf"); private ResourceResolverFactory resolverFactory; // 注入ResourceResolverFactory来获取资源元数据 public ResourceCacheFilter(ResourceResolverFactory resolverFactory) { this.resolverFactory = resolverFactory; } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; // 获取请求资源的后缀 String path = req.getRequestURI(); int lastDotIndex = path.lastIndexOf('.'); if (lastDotIndex != -1) { String extension = path.substring(lastDotIndex + 1).toLowerCase(); if (CACHEABLE_EXTENSIONS.contains(extension)) { // 获取资源的最后修改时间,用于生成ETag和Last-Modified头 try (ResourceResolver resolver = resolverFactory.getResourceResolver(Map.of(ResourceResolverFactory.SUBSERVICE, "cache-filter"))) { Resource resource = resolver.getResource(path); if (resource != null) { long lastModified = resource.getValueMap().get("jcr:lastModified", 0L); String eTag = "\"" + lastModified + "\""; // 设置核心缓存头 res.setHeader("Cache-Control", "public, max-age=86400"); res.setHeader("ETag", eTag); res.setDateHeader("Last-Modified", lastModified); } } catch (Exception e) { // 异常时降级处理,不设置缓存头 e.printStackTrace(); } } } // 继续执行后续请求链路 chain.doFilter(request, response); } @Override public void init(FilterConfig filterConfig) throws ServletException {} @Override public void destroy() {} }
关键说明:
sling.filter.pattern:指定要拦截的资源路径,比如/content/dam/my-site/.*只缓存特定站点的资源Cache-Control头配置:public:允许浏览器和中间缓存(如CDN、Dispatcher)存储该资源max-age=86400:浏览器可缓存该资源1天(86400秒),之后才会重新请求- 如果资源是不会变的(比如版本化命名的
image.v2.png),可以加immutable,浏览器会彻底缓存,不会发送验证请求
- ETag和Last-Modified:这两个头用于协商缓存,当缓存过期后,浏览器会发送带有
If-None-Match(对应ETag)或If-Modified-Since的请求,服务器判断资源未变化时返回304,节省带宽。
方案3:自定义Servlet返回资源时设置头
如果是你自己写的Servlet返回动态资源(比如生成的图片),直接在doGet方法里设置响应头:
import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.SlingHttpServletResponse; import org.apache.sling.api.servlets.SlingSafeMethodsServlet; import org.osgi.service.component.annotations.Component; @Component( service = SlingSafeMethodsServlet.class, property = { "sling.servlet.paths=/bin/my-custom-image", "sling.servlet.methods=GET" } ) public class CustomImageServlet extends SlingSafeMethodsServlet { @Override protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) { // 设置缓存头 response.setHeader("Cache-Control", "public, max-age=604800"); // 缓存1周 response.setHeader("ETag", "\"custom-image-v1\""); // 版本化ETag // 写入资源内容到response输出流 // ... } }
验证缓存是否生效
打开浏览器开发者工具(F12),切换到Network标签:
- 第一次请求资源,查看响应头是否有
Cache-Control、ETag等字段,状态码为200 - 刷新页面,再次请求该资源,状态码应为304(协商缓存)或直接从本地缓存读取(Size列显示
memory cache/disk cache)
常见坑要避开
- 不要设置
Cache-Control: no-cache或no-store:这会禁止浏览器缓存,和需求相反 - 动态参数问题:如果资源URL带动态参数(比如
/image.png?timestamp=123),浏览器会把不同参数的URL视为不同资源,导致缓存失效。尽量用版本化文件名(如image.v2.png)代替动态参数 - Dispatcher缓存冲突:确保Dispatcher传递缓存头到浏览器,不要在配置中移除这些头
- 资源权限:如果资源需要登录才能访问,把
public改为private(仅当前用户浏览器可缓存)
内容的提问来源于stack exchange,提问作者Dise
相关产品推荐
相关产品推荐

