猿问

是否可以手动缓存整个网页?

我需要缓存一个网页,然后为了将来的请求,检查缓存(使用 url 作为键),如果找到,则从缓存中返回网页而不是发出请求。


我正在使用 Smiley 的 ProxyServlet,servlet 写入 OutputStream 的方法似乎非常适合缓存。我只添加了两行代码:


/**

 * Copy response body data (the entity) from the proxy to the servlet client.

 * TODO: CACHE entity here for retrieval in filter

 */

protected void copyResponseEntity( HttpResponse proxyResponse, HttpServletResponse servletResponse,

        HttpRequest proxyRequest, HttpServletRequest servletRequest ) throws IOException

{

    HttpEntity entity = proxyResponse.getEntity();

    if ( entity != null )

    {

        String key =  getCurrentUrlFromRequest( servletRequest );  // 1

        basicCache.getCache().put( key, proxyResponse.getEntity() ); // 2

        OutputStream servletOutputStream = servletResponse.getOutputStream();

        entity.writeTo( servletOutputStream );

    }

}

它有点管用,它确实将 HttpEntity 存储在缓存中。但是当我返回到浏览器并再次请求相同的 url 时,当代码返回到我的过滤器中时,我使用 url 作为键获得了 HttpEntity,并将其写入响应,但是我得到了一个“Stream closed “ 错误:


java.io.IOException: Stream closed

    at java.base/java.util.zip.GZIPInputStream.ensureOpen(GZIPInputStream.java:63) ~[na:na]

    at java.base/java.util.zip.GZIPInputStream.read(GZIPInputStream.java:114) ~[na:na]

    at java.base/java.io.FilterInputStream.read(FilterInputStream.java:107) ~[na:na]

    at org.apache.http.client.entity.LazyDecompressingInputStream.read(LazyDecompressingInputStream.java:64) ~[httpclient-4.5.9.jar:4.5.9]

    at org.apache.http.client.entity.DecompressingEntity.writeTo(DecompressingEntity.java:93) ~[httpclient-4.5.9.jar:4.5.9]

    at com.myapp.test.foo.filters.TestFilter.doFilter(TestFilter.java:37) ~[classes/:na]


我坚持使用这种非常本地化的/手动的/任何您想称之为缓存的东西——我不能使用明显的“只需连接 ehcache/redis/任何东西并让它做它的事情”。因此,虽然我知道那些优秀的缓存可以缓存整个网页,但我不知道它们是否允许我以这种公认的不寻常方式工作。

所以我希望 SO 可以告诉我如何完成这项工作。我首先尝试在 ConcurrentHashMap 中连接我的基本缓存,但这也不起作用,所以我想看看我是否可以利用大型缓存枪所拥有的任何魔力,但到目前为止我不能。



繁花如伊
浏览 171回答 2
2回答

江户川乱折腾

课堂上TestFilter,能不能在这一行下个断点调试一下?HttpEntity page = (HttpEntity) basicCache.getCache().get( url );底层流可能不处于您实际执行的状态:page.writeTo( servletOutputStream );本质上,声明PrintWriter您控制状态,从缓存中获取内容,写入响应,然后关闭编写器。

慕桂英546537

正如这里已经说过的, 应该在 HttpServletResponse.getOutputStream()/.getWriter() 上调用 .close() 吗?您最好为您的 servlet 实现一个包装器,详见下文。https://www.oracle.com/technetwork/java/filters-137243.html#72674

明月笑刀无情

我想知道为什么所有这些很好的答案都同意包装 HttpServletResponse 而我无法让它工作——我觉得自己像个白痴。它没有用,因为对 wrap 的响应中没有任何内容。页面内容从一开始就全部在 HttpEntity 中: HttpEntity entity = proxyResponse.getEntity();在我意识到追逐 servlet 响应/请求不是答案之后,我很幸运地发现:org.apache.http.entity.BufferedHttpEntity包装一个 HttpEntity 以便您可以重复获取内容),但是它是在正确的对象上进行的。所以上面的第一个方法只是稍微调整了一下,它仍然代理得很好:    if ( entity != null )    {        String key =  getCurrentUrlFromRequest( servletRequest );        OutputStream servletOutputStream = servletResponse.getOutputStream();        BufferedHttpEntity wrapper = new BufferedHttpEntity( entity );        basicCache.getCache().put( key, wrapper );        wrapper.writeTo( servletOutputStream );    }将 BasicCache 更改为期望一个 String 和一个 BufferedHttpEntity,然后对于后续请求,在过滤器中从缓存中获取 BufferedHttpEntity,完成所有工作的行与上面的最后一行相同:    if ( null != page )    {                   OutputStream servletOutputStream = servletResponse.getOutputStream();        page.writeTo( servletOutputStream );  // bingo    }    else    {        filterChain.doFilter( servletRequest, servletResponse );    }感谢大家的帮助!
随时随地看视频慕课网APP

相关分类

Java
我要回答