Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to specify specific cache key ? #501

Closed
anthony-skr opened this issue Jun 18, 2015 · 24 comments
Closed

How to specify specific cache key ? #501

anthony-skr opened this issue Jun 18, 2015 · 24 comments
Labels

Comments

@anthony-skr
Copy link

Hi,

In my app I display photo via an Url with token. For a same photo I can have multiple URL :

I would like to store in cache photo_1 and use photo_1 cache whatever the URL.

How can I do this ?

@sjudd sjudd added the question label Jun 21, 2015
@sjudd
Copy link
Collaborator

sjudd commented Jun 21, 2015

It's not trivial to do this, but you can create a custom ModelLoader and DataFetcher, wrap an existing HttpFetcher, delegate all methods other than getId(), and override getId() to return your photo id.

For an example of how to base one ModelLoader on another, you can take a look at BaseGlideUrlLoader and the wiki page on downloading custom sizes.

@anthony-skr
Copy link
Author

It seems getId() mechanism is not used anymore on Glide :
https://github.com/bumptech/glide/search?utf8=%E2%9C%93&q=getId&type=Code

:-(

@sjudd
Copy link
Collaborator

sjudd commented Jun 22, 2015

Switch to the 3.0 branch, my comments above are assuming you're using Glide 3.6.

@TWiStErRob
Copy link
Collaborator

You can only search the default branch online, which is the master by default.

@sjudd can you set default to branch 3.0 while 4.0 is in alpha stage? https://help.github.com/articles/setting-the-default-branch/

@anthony-skr
Copy link
Author

Thank you for your useful help.

I reached my objective with GlideUrl :

public class GlideUrlNoToken extends GlideUrl {
    public GlideUrlNoToken(String url) {
        super(url);
    }

    public GlideUrlNoToken(String url, Headers headers) {
        super(url, headers);
    }

    public GlideUrlNoToken(URL url) {
        super(url);
    }

    public GlideUrlNoToken(URL url, Headers headers) {
        super(url, headers);
    }

    @Override
    public String getCacheKey() {
        String url = toStringUrl();
        if (url.contains("?")) {
            return url.substring(0, url.lastIndexOf("?"));
        } else {
            return url;
        }
    }
}

@trungptdhcn
Copy link

I have same issue. I use amazon server to store image. Url from amazon server change after 15 secound, I can't cache image with URL. Please sugguest for me idea to reslove this issue. Thank

@TWiStErRob
Copy link
Collaborator

@trungptdhcn Assuming you mean that the image contents returned by the URL changes, check out the wiki and try adding

.signature(new StringSignature(String.valueOf(System. currentTimeMillis() / (15 * 1000))))

to your Glide line. You'll have to make sure to reload the image (re-call, Glide...into) every 15 seconds if you want continuous display.

If the whole URL changes than you need to know the new URL and use that.

It's probably worth turning off caching (.diskCacheStrategy(NONE)) in both cases since it's really unlikely to reuse the cached image and it'll just push other images out of cache.

@TWiStErRob
Copy link
Collaborator

@trungptdhcn If you have something that maps to the URL like a profile ID for example, then you need to create your own model and ModelLoader and use that instead. That way the model will be used as cache key (=DataFetcher.getId()) and not the resulting URL which changes all the time. See Sam's comment above.

@trungptdhcn
Copy link

Yep, only url of image change after 15 seconds and I want cache image with my imageID. Thank for reply. I will implement this. :)

@trungptdhcn
Copy link

Can you give me example use ModelLoader? I understood your idea, but I can't find tutorial for used ModelLoader. Thank

@TWiStErRob
Copy link
Collaborator

@trungptdhcn here's one possible solution (untested), based on #501 (comment) and the requirement I understood from your comments.

// usage
Glide.with(this).load(new Image(234453)).into(imageView);

// TODO https://github.com/bumptech/glide/wiki/Configuration#creating-a-glidemodule
public class AmazonModule implements GlideModule {
    @Override public void applyOptions(Context context, GlideBuilder builder) { }
    @Override public void registerComponents(Context context, Glide glide) {
        glide.register(Image.class, InputStream.class, new AmazonImageLoader.Factory());
    }
}

public class Image {
    int imageId;
    public Image(int id) { this.imageId = id; }
    public String getUrl() {
        return "http://s3.foo/bar_" + imageId + ".jpg?time=" + getTimeKey(System.currentTimeMillis());
    }
    private String getTimeKey(long time) {
        return String.valueOf(time / 15000); // this is the changing part of the url
    }
}

public class AmazonImageLoader implements StreamModelLoader<Image> {
    @Override public DataFetcher<InputStream> getResourceFetcher(final Image model, int width, int height) {
        return new HttpUrlFetcher(new GlideUrl(model.getUrl())) {
            @Override public String getId() {
                return "AmazonImage::" + model.imageId;
            }
        };
    }

    public static class Factory implements ModelLoaderFactory<Image, InputStream> {
        @Override public ModelLoader<Image, InputStream> build(Context context, GenericLoaderFactory factories) {
            return new AmazonImageLoader();
        }
        @Override public void teardown() { /* no op */ }
    }
}

Check out https://groups.google.com/d/msg/glidelibrary/wneghdP2v2o/rHsVen8G8fEJ for a more complex, but unrelated example.

@trungptdhcn
Copy link

Thank for your answer!

@bhargavms
Copy link
Contributor

Why is @anthony-skr 's solution not considered here @TWiStErRob ?

@TWiStErRob
Copy link
Collaborator

The version I gave is more explicit and dynamic in terms of implementation. It's not hacking existing features. I always prefer building a URL, rather than breaking it down to pieces by parsing. I think historically @anthony-skr's solution wasn't working for @trungptdhcn and that's why I elaborated.

However I'm not sure I understood your concern fully, @bhargavms.

@bhargavms
Copy link
Contributor

I meant this thread was opened for the context where a part of the URL changed but there was some parse-able part of the URL which remains constant, (this situation is very common when using Amazon S3 servers where the file name remains constant), so I wanted an opinion from a contributor (you in this case) for the solution proposed by the author in handling this situation, I feel the solution provided by anothony is much easier to implement.

@MichaelJokAr
Copy link

how to use this in v4?

@the-kenneth
Copy link

There are a number of situations where easily being able to set the cache key would be very helpful. Are there any plans to implement such a feature?

@CarsonRedeye
Copy link

CarsonRedeye commented Jan 23, 2019

Thank you for your useful help.

I reached my objective with GlideUrl :

public class GlideUrlNoToken extends GlideUrl {
    public GlideUrlNoToken(String url) {
        super(url);
    }

    public GlideUrlNoToken(String url, Headers headers) {
        super(url, headers);
    }

    public GlideUrlNoToken(URL url) {
        super(url);
    }

    public GlideUrlNoToken(URL url, Headers headers) {
        super(url, headers);
    }

    @Override
    public String getCacheKey() {
        String url = toStringUrl();
        if (url.contains("?")) {
            return url.substring(0, url.lastIndexOf("?"));
        } else {
            return url;
        }
    }
}

Can this approach still be used with Glide 4.8?

@DennyWeinberg
Copy link

DennyWeinberg commented May 6, 2019

Awesome! I used @anthony-skr's solution and it works with v4.9:

public class GlideUrlNoToken extends GlideUrl {
    public String cachekey;

    public GlideUrlNoToken(Uri uri, String cacheKey) {
        super(uri.toString());
        this.cachekey = cacheKey;
    }

    @Override
    public String getCacheKey() {
        return cachekey;
    }
}

This is so good, you can specifiy your own cache key!

@hzardaryan
Copy link

hzardaryan commented Jun 11, 2019

Awesome! I used @anthony-skr's solution and it works with v4.9:

public class GlideUrlNoToken extends GlideUrl {
    public String cachekey;

    public GlideUrlNoToken(Uri uri, String cacheKey) {
        super(uri.toString());
        this.cachekey = cacheKey;
    }

    @Override
    public String getCacheKey() {
        return cachekey;
    }
}

This is so good, you can specifiy your own cache key!

Does not this cause another trouble? HttpGlideUrlLoader caches the models and it uses "getCacheKey()" to get the Model from the map. As a result your app might use the model with expired url in case app was not closed meanwhile (the Model map is cached in-memory).

To reproduce this, open your app, access the image loading activity/fragment, notice the image being loaded, turn off internet access, clean app's cache (without killing the app). Now go back to your app, notice that images are not there (to make sure images are lot loaded from the cache). Wait as long as you need the url to be expired, turn your internet access back on and notice that image is never loaded (in logs you can see that expired url is being used). Of course if you kill now the app and run it again, the photos will be loaded.

@bhargavms
Copy link
Contributor

@hzardaryan but that is so hard to reproduce that I dont think any user is ever going to see that bug no? But its a very nice catch!

@thirtyyuan
Copy link

I have find IntegerVersionSignature that is official demo.

@oxied
Copy link

oxied commented Sep 8, 2019

My version:

package com.myapp.android.util

import android.content.Context
import android.net.Uri
import com.bumptech.glide.Glide
import com.bumptech.glide.RequestManager
import com.bumptech.glide.load.model.GlideUrl
import com.myapp.android.R


fun <T> Context.glideNoPlaceholder(data: T?) =
    Glide.with(this).loadNoToken(data).dontAnimate()

fun <T> Context.glideWhitePlaceholder(data: T?) =
    glideNoPlaceholder(data).placeholder(android.R.color.white)

fun <T> Context.glide(data: T?) =
    glideNoPlaceholder(data).placeholder(R.drawable.im_logo)

private fun <T> RequestManager.loadNoToken(data: T?) =
    if ((data is String || data is Uri)
        && data.toString().isNotBlank())
        load(GlideUrlNoToken(data.toString()))
    else
        load(data)

private class GlideUrlNoToken(url: String) : GlideUrl(url) {

    private val cacheKey: String?

    init {
        val index = url.indexOfAny(listOf("jpeg", "jpg"), ignoreCase = true)
        cacheKey = if (index > 0)
            url.substring(0, index)
        else
            null
    }

    override fun getCacheKey(): String {
        return cacheKey ?: super.getCacheKey()
    }

}

@thirtyyuan
Copy link

@oxied I think this basically works the same as URL as a cache key, just ignores two specific image formats.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests