You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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标签:

  1. 第一次请求资源,查看响应头是否有Cache-Control、ETag等字段,状态码为200
  2. 刷新页面,再次请求该资源,状态码应为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:52:16