Httpclient doesn't correctly caches responses

对着背影说爱祢 提交于 2019-12-11 01:59:18

问题


I'm trying to use apache httpclient to load images with caching. After request the file is saved but after repeating same request it begins to download again and new file saved as cache. So cached images not reused. And not deleting.

File names differ only by hash
1389449846612.0000000000000001-3f1e8b88.localhost.-images-goods-212250-7841874.jpg
1389449952782.0000000000000001-5720e341.localhost.-images-goods-212250-7841874.jpg

I want, that image will be loaded once and be able to show cached image even when there is no connection to internet.

Here my code

RequestConfig config = RequestConfig.custom()
                .setConnectTimeout(30000)
                .setSocketTimeout(30000)
                .setProxy(getProxy())
                .build();

CacheConfig cacheConfig = CacheConfig.custom()
        .build();

CloseableHttpClient client = CachingHttpClientBuilder.create()
        .setCacheDir(new File("/sdcard/Android/data/com.myapp/cache/"))
        .setCacheConfig(cacheConfig)
        .setDefaultRequestConfig(config)
        .build();

HttpGet request = new HttpGet(imageUri);
HttpCacheContext context = HttpCacheContext.create();
CloseableHttpResponse response = client.execute(request, context);

This is image response headers

Cache-Control:max-age=604800
Connection:keep-alive
Content-Length:449512
Content-Type:image/jpeg
Date:Sat, 11 Jan 2014 15:03:21 GMT
Expires:Sat, 18 Jan 2014 15:03:21 GMT
Last-Modified:Tue, 12 Jul 2011 19:40:44 GMT

回答1:


First problem is that i'm created new httpclient every time before download, so every time was created new instance of HttpCacheStorage, this is why files had different names.
Second, default HttpCacheStorage stores info of downloaded data just in LinkedHashMap so after every new launch of app cacheStorage don't know anything about cached data in previous launch.
Solutions was to create own HttpCacheStorage, wich will save cached data to file system and will get data from files when response can be got from cache.

I just added one line to CachingHttpClientBuilder - setHttpCacheStorage

CachingHttpClientBuilder.create()
                        .setCacheConfig(cacheConfig)
                        .setHttpCacheStorage(new ImagesCacheStorage(cacheConfig, cacheDir))
                        .setDefaultRequestConfig(config)
                        .build();

and created new class FileCacheStorage

import myapp.org.apache.http.client.cache.HttpCacheEntry;
import myapp.org.apache.http.client.cache.HttpCacheUpdateCallback;
import myapp.org.apache.http.impl.client.cache.CacheConfig;
import myapp.org.apache.http.impl.client.cache.ManagedHttpCacheStorage;

public class FileCacheStorage extends ManagedHttpCacheStorage {

    private File mCacheDir;

    public FileCacheStorage(final CacheConfig config, File cacheDir) {
        super(config);
        mCacheDir = cacheDir;
    }

    @Override
    public HttpCacheEntry getEntry(final String url) throws IOException {
        HttpCacheEntry entry = super.getEntry(url);
        if (entry == null) {
            entry = loadCacheEnrty(url);
        }
        return entry;
    }

    @Override
    public void putEntry(final String url, final HttpCacheEntry entry) throws IOException {
        super.putEntry(url, entry);
        saveCacheEntry(url, entry);
    }

    @Override
    public void removeEntry(final String url) throws IOException {
        super.removeEntry(url);
        File cache = getCacheFile(url);
        if (cache != null && cache.exists()) {
            cache.delete();
        }
    }

    @Override
    public void updateEntry(
            final String url,
            final HttpCacheUpdateCallback callback) throws IOException {
        super.updateEntry(url, callback);
        HttpCacheEntry entry = loadCacheEnrty(url);
        if (entry != null) {
            callback.update(entry);
        }
    }

    private void saveCacheEntry(String url, HttpCacheEntry entry) {
        ObjectOutputStream stream = null;
        try {
            File cache = getCacheFile(url);
            stream = new ObjectOutputStream(new FileOutputStream(cache));
            stream.writeObject(entry);
            stream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private HttpCacheEntry loadCacheEnrty(String url) {
        HttpCacheEntry entry = null;
        File cache = getCacheFile(url);
        if (cache != null && cache.exists()) {
            synchronized (this) {
                ObjectInputStream stream = null;
                try {
                    stream = new ObjectInputStream(new FileInputStream(cache));
                    entry = (HttpCacheEntry) stream.readObject();
                    stream.close();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (StreamCorruptedException e) {
                    e.printStackTrace();
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return entry;
    }

    private File getCacheFile(String url) {
        return new File(mCacheDir, MD5.getHash(url));
    }

}

As you can see Apache classes have package name with prefix myapp. I got errors when tryed to use origin jar files, I think it's because many classes are already present in Android. So I combined few jar files from apache and made from them one jar with that prefix. If someone have a better solution let me know. Hope it help someone.



来源:https://stackoverflow.com/questions/21064180/httpclient-doesnt-correctly-caches-responses

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!