当前位置: 首页 > 知识库问答 >
问题:

JavaFx 8 WebEngine-通过用户数据目录缓存图像?

童琪
2023-03-14

我是JavaFx新手,尝试在我的应用程序中实现它的浏览器。由于在每次新启动时重新加载所有图像相当耗时,我想将它们存储在一些缓存目录中,但一直未能做到这一点。我尝试使用setUserDataDirectory(…)

然后我发现这是一个很好的帖子,但首先,我还不允许在那里发表评论,其次,它似乎只针对JavaFX2.2。那里发布的内容对JavaFx 8仍然适用吗?如果是这样:有没有一种简单的方法来实现这样的URL连接缓存?

非常感谢您的帮助:)

共有2个答案

邵璞
2023-03-14

据我所知,这仍然有效。如果您只想实现一个特定的目的,这并不困难,但通用解决方案将更具挑战性。

袁良弼
2023-03-14

因此,在深入研究这个问题并学习了URLConnection类两天后,我终于能够提出我自己的(大概是粗糙的)实现,我想在这里与大家分享,以防任何像我这样知之甚少的人在这个线程上绊倒。同样,基本思想是在缓存中只放置特定的文件类型,而不是全部。我选择存储jpg、png、gif和js文件,因为我认为它们是负载最重的文件,尽管其他文件格式也应该可以。第一件事:浏览器。getEngine()。setUserDataDirectory(…) 肯定做不到这项工作。我仍然不知道它有什么好处,但它肯定不会存储图像文件

相反,我所做的基本上是创建5个类:

  • 缓存资源(CachedResource):由保存资源原始数据的字节[]数组和一些元信息(头字段,最后修改)组成
  • 资源缓存:保存所有当前缓存的资源对象
  • MyHttpUrlConnection(扩展了sun.net.www.protocol.http.HttpURLConnection):一个包装类,负责检索其给定URL指向的文件。它实现了所有网络魔法
  • CachedUrlConnection(扩展了java.net.URLConnection):一个(几乎)空的实现,它已经拥有了我们需要的所有数据,只等待系统调用它
  • MyUrlConnectionHandler(扩展了sun.net.www.protocol.http.Handler):这个类在应用程序启动时注册,并决定何时使用哪个URL连接(见下文)

ResourceCacheCachedResourcesCachedUrlConnection类相当小且易于编写。我设计了资源缓存以将url的资源与其对应的CachedResources对象映射,因此:ConconttHashMap

然后,我实现了CachedUrlConnection类,如下所示:

public class CachedUrlConnection extends URLConnection {

    private CachedResource resource;
    private ByteArrayInputStream inputStream;

    /* Constructors */
    public CachedUrlConnection(URL url, CachedResource resource) throws IOException {
        super(url);
        this.resource = resource;
        this.inputStream = new ByteArrayInputStream(resource.getByteData());
    }

    @Override
    public void connect() throws IOException {
        // No need to do anything.
    }

    /* Object Methods */

    /* Getters and Setters */
    @Override
    public String getHeaderField(int index) { ... }

    @Override
    public String getHeaderField(String key) { ... }

    @Override
    public Map<String, List<String>> getHeaderFields() { ... }

    @Override
    public InputStream getInputStream() throws IOException {
        return inputStream; // <---- Here, the system can grab the data.
    }
}

当查看URLConnection的源代码(例如这里)时,您会很快注意到它的大多数方法实现都是傻瓜,要么返回null,要么抛出未知服务异常。

这很重要:我不知道你需要实现其中的哪一个!

为了找到答案,我使用了MyHttpUrlConnection类,并将其添加到几乎每个函数中

System.out.println("function xyz called!");
super.xyz();

但我很懒,没有检查所有的。到目前为止,一切似乎都很顺利

下一个类是MyHttpUrlConnection。我不能百分之百确定我是否真的需要覆盖HttpURLConnection类,但我还是这样做了,因为它有一个受保护的构造函数,该构造函数将被一个新的sun隐式调用。网www.protocol。http。处理程序。该处理程序显然不会遵循我们的http策略,所以我只是想确定一下(参见sourcecode line 801)。因此,该类看起来相当空:

public class MyHttpUrlConnection extends HttpURLConnection {

    protected MyHttpUrlConnection(URL url, Handler handler) {
        this(url, null, handler);
    }

    public MyHttpUrlConnection(URL url, Proxy proxy) {
        this(url, proxy, new MyUrlConnectionHandler()); // <--- No way sneaking around^^
    }

    protected MyHttpUrlConnection(URL url, Proxy proxy, Handler handler) {
        super(url, proxy, handler);
    }

    public MyHttpUrlConnection(URL url, String host, int port) {
        this(url, new Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved(host, port))); // Taken over from the HttpURLConnection sourcecode.
    }
}

现在是最重要的部分:MyUrlConnectionHandler。再次检查此线程的放置位置。类本身需要的只是覆盖openConnection(URL, Proxy)函数。在发布我的代码之前,我将向您介绍它的作用。

  1. 如果给定的URL是jpg、png、...文件:
  2. 使用MyHttpUrlConnection对象获取服务器上资源的最后修改日期。这应该只调用标头,而不是整个资源。否则,我们不会赢得任何东西。信用归这个线程。不过,我不完全确定我是否在这里正确关闭了URLConnection。如果有疑问,最好仔细检查;)
  3. 如果缓存中没有资源或缓存中的资源已过期:
  4. 关闭迷你连接并打开一个“正确”的连接来下载整个东西。
  5. 创建一个新的CachedResources对象并将其添加到缓存中。
  6. 也关闭那个新连接。
  7. 返回一个保存数据的新CachedUrlConnection对象。这可能看起来有点愚蠢,因为我们已经拥有了一切,但该函数需要返回一个URLConnection
  8. 如果有任何异常或者我们根本没有处理jpg、png、...文件,返回一个“默认”MyHttpUrlConnection对象以正常处理URL。

相应的代码如下所示。注意,我使用了Apache的org。阿帕奇。平民io。IOUtils:

@Override
protected URLConnection openConnection(URL url, Proxy proxy) throws IOException {

    try {

        // Is this some resource that we'd like to cache?
        if (ResourceCache.isCachableURL(url)) {

            // Retrieve whatever is in the cache first.
            ResourceCache cache = ResourceCache.getInstance();
            CachedResource resource = cache.getCachedResource(url);

            // Open a connection to the server to at least check for the last-modified field.
            MyHttpUrlConnection conn = new MyHttpUrlConnection(url, this); // Don't use URL#openConnection to avoid looping!
            conn.setRequestMethod("HEAD");
            conn.connect();
            long lastModified = conn.getLastModified();

            // Did we get the last-modified value at all?
            if (lastModified == 0) {
                throw new Exception("No last-modified value could be read! \n\t" + url);
            }

            // Resource not cached or out of date?
            if (resource == null || resource.getLastModified() < lastModified) {

                conn = new MyHttpUrlConnection(url, this);
                conn.connect();
                InputStream input = conn.getInputStream();
                byte[] data = IOUtils.toByteArray(input);
                Map<String, List<String>> headerFields = conn.getHeaderFields();
                IOUtils.closeQuietly(input); 

                resource = new CachedResource(url.getFile(), data, headerFields, lastModified); // I use url.getFile() to store the file on my hard drive.
                cache.addCachedResource(url, resource);
            }

            return new CachedUrlConnection(url, resource);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }

    // Return the default HttpURLConnection in our wrapper class.
    return new MyHttpUrlConnection(url, proxy, this);
}

最后一件事:为了安全起见,不要在MyUrlConnectionHandler#openConnection函数中使用URL#openConnection方法。以这种方式写下来,可以很明显地看出原因,但今天,我花了很长时间才弄清楚无限循环来自何处。使用构造函数并调用connect()

我希望这对任何人都有帮助,否则这对我来说都是一个很好的锻炼^^

 类似资料:
  • 问题内容: 我们希望在生产部署中缓存崩溃,但不要浪费大量时间来弄清楚这样做的系统。我的想法是将具有当前版本号的paras应用于css和js文件的末尾: 两个问题:这会有效地打破缓存吗?由于参数表明这是动态内容,因此该参数会导致浏览器从不缓存该URL的响应吗? 问题答案: 参数表示查询字符串,因此浏览器将认为这是从到的新路径。因此导致它从文件而不是从缓存加载。如你所愿。 而且,浏览器将假定下次调用时

  • 本文向大家介绍vue项目优化之通过keep-alive数据缓存的方法,包括了vue项目优化之通过keep-alive数据缓存的方法的使用技巧和注意事项,需要的朋友参考一下 <keep-alive>是Vue的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM。 <keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 <transition> 相似,<ke

  • 问题内容: 我们希望在生产部署中缓存崩溃,但不要浪费大量时间来弄清楚这样做的系统。我的想法是将具有当前版本号的paras应用于css和js文件的末尾: 两个问题:这会有效地打破缓存吗?由于参数表明这是动态内容,因此该参数会导致浏览器从不缓存该URL的响应吗? 问题答案: 参数表示查询字符串,因此浏览器将认为这是从到的新路径。因此导致它从文件而不是从缓存加载。如你所愿。 而且,浏览器将假定下次调用时

  • This is the name of the directory where template caches are stored. By default this is "./cache", meaning that it will look for the cache directory in the same directory as the executing php script. Y

  • 假如要开发一个电子商务网站,商品的类别数据Category,是很少发生变化的,而几乎在每个网页,都需要显示商品类别。如果每次都要从mongoDB数据库中查询出商品类别,显然不是一个好主意。 bugu-mongo-cache模块,就是针对这种应用场景的:某个表的数据量很小,很少发生变化,但需要频繁的查询。这样的数据,我们希望能够“常驻内存”,既减少数据库的查询次数,又加快对用户的响应速度。 初始化

  • 数据缓存是指将一些 PHP 变量存储到缓存中,使用时再从缓存中取回。 它也是更高级缓存特性的基础,例如查询缓存 和内容缓存。 如下代码是一个典型的数据缓存使用模式。 其中 $cache 指向缓存组件: // 尝试从缓存中取回 $data $data = $cache->get($key); if ($data === false) { // $data 在缓存中没有找到,则重新计算它