2015-09-21 2 views
0

Я новичок в JavaFx и попытался реализовать его браузер в своем приложении. Поскольку перезагрузка всех изображений при каждом новом запуске занимает довольно много времени, Я хотел бы сохранить их в каком-то каталоге кеша, но не смог этого сделать. Я пробовал использовать setUserDataDirectory(...), но это приводит только к пустой папке с именем localstorage и к файлу .lock.JavaFx 8 WebEngine - кэширование изображений через каталог пользовательских данных?

Я тогда нашел это SO thread, но, во-первых, мне еще не разрешено прокомментировать там, а во-вторых, это только похоже на JavaFx 2.2. Есть ли то, что было опубликовано там, все еще верно для JavaFx 8? Если да: существует ли простой способ реализовать такой кеш URLConnection?

Большое спасибо за любую помощь :)

ответ

1

Насколько я знаю, что это остается в силе. Если вы хотите реализовать это только для определенной цели, это не так сложно, но решение общей цели будет более сложным.

+0

Ну сейчас я был бы счастлив, если бы я мог, по крайней мере сохранить нагрузки тяжелых графических файлов на жестком диске а затем загрузите их оттуда. Однако я не знаю, как это сделать. Можно ли расширить «net.www.protocol.http.Handler» или «HttpURLConnection» и «просто» изменить часть своей функциональности в соответствии с моими потребностями? (Мне нужно только поддерживать http на данный момент.) – dadoosh

2

Итак, после 2-х дневных исследований проблемы и изучения класса URLConnection, я наконец-то смог придумать свою собственную (предположительно грубую) реализацию, которую я хотел бы поделиться здесь, на случай, если кто-либо с таким маленьким поскольку я случайно наткнулся на эту тему. Опять же, основная идея заключалась в том, чтобы помещать только определенные типы файлов в кеш, а не все. Я решил хранить файлы jpg, png, gif и js, так как я считал их самыми загруженными, хотя каждый другой формат файла также должен быть возможен. Прежде всего: browser.getEngine().setUserDataDirectory(...) определенно НЕ выполняет эту работу. Я до сих пор понятия не имеют, что это хорошо для, но это, конечно, не хранить файлы изображений%)

Вместо того, что я был в основном создание 5 классов:

  • CachedResource: Состоит из byte[] массива холдинг необработанные данные ресурса и некоторая метаинформация (поля заголовка, lastModified)
  • ResourceCache: Сохраняет все кэшируемые объекты ресурсов.
  • MyHttpUrlConnection (extends sun.net.www.protocol.http.HttpURLConnection): Класс оболочки, который отвечает за получение файла, на который указывает данный URL. Он делает всю сетевую магию.
  • CachedUrlConnection (extends java.net.URLConnection): (почти) пустая реализация, которая уже имеет все необходимые данные и только ждет, когда система ее вызовет.
  • MyUrlConnectionHandler (распространяется sun.net.www.protocol.http.Handler): Этот класс зарегистрирован при запуске приложения и принимает решение о том, когда следует использовать URLConnection (см. Ниже).

The ResourceCache, CachedResource и CachedUrlConnection классы достаточно малы и легко писать. Я разработал кеш ресурсов для сопоставления ресурса url с его соответствующим объектом CachedResource, таким образом: ConcurrentHashMap<URL, CachedResource> плюс getter и addResource(...). Я добавил к нему некоторые другие вещи, такие как хранение файлов локально, но это приводит к отключению темы.

Затем я реализовал 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 (например here), вы быстро заметите, что большинство его реализаций метода являются манекены, которые либо возвращают null или бросьте UnknownServiceException.

Это важно: я не знаю, какие именно из них вам нужно реализовать!

Для того, чтобы выяснить, я использовал MyHttpUrlConnection класс и добавил почти каждый функции

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

, но я был ленивым и не проверял их все. Пока что все работает нормально%)

Следующий класс был MyHttpUrlConnection. Я не уверен на 100%, если мне действительно нужно перезаписать класс HttpURLConnection, но я так и сделал, потому что у него есть конструктор protected, который будет неявно вызываться с новым sun.net.www.protocol.http.Handler. Этот обработчик, очевидно, не будет следовать нашей политике http, поэтому я просто хотел быть уверенным (cf. sourcecode строка 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. Опять же, отметьте this thread о том, куда его поместить. Всем классом необходимо перезаписать функцию openConnection(URL, Proxy). Прежде чем отправлять мой код, я дам вам полный контроль над тем, что он делает.

  1. Если данный URL является JPG, PNG, ... Файл:
  2. Используйте MyHttpUrlConnection объект, чтобы получить дату последнего ресурса модификации на сервере. это должно вызывать только заголовок и НЕ весь ресурс. В противном случае мы ничего бы не выиграли. Кредиты идут до this thread. Я не совсем уверен, хотя я закрываю URLConnection здесь. Лучше дважды проверить, если у вас есть сомнения;)
  3. Если в кеше нет ресурса или ресурс в кеше устарел:
  4. Закройте мини-соединение и откройте «правильный», чтобы загрузить все это ,
  5. Создайте новый объект CachedResource и добавьте его в кеш.
  6. Закройте это новое соединение, тоже.
  7. Возвращает новый объект CachedUrlConnection, содержащий данные. Это может показаться немного глупым, поскольку у нас уже есть все, но функция должна вернуть URLConnection.
  8. Если есть какое-либо исключение или мы просто не имеем дело с файлом jpg, png, ..., возвращаем объект «default» MyHttpUrlConnection для обработки URL-адреса в обычном режиме.

И соответствующий код выглядит следующим образом.Обратите внимание, что я использовал Apache org.apache.commons.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); 
} 

последнее: Чтобы быть на безопасной стороне, НИКОГДА не используйте метод URL#openConnection внутри функции MyUrlConnectionHandler#openConnection. Написание этого способа делает это довольно очевидным, почему, но сегодня мне потребовалось довольно много времени, чтобы выяснить, откуда пришел бесконечный цикл из%). Вместо этого используйте конструктор и вызовите connect().

Я надеюсь, что это когда-нибудь помочь любому, в противном случае это было хорошее упражнение для меня ^^

Смежные вопросы