Glide: Loading and decrypting image

Created on 20 Jan 2016  路  20Comments  路  Source: bumptech/glide

hello.

I have to load images from multiple sources. i want to use glide + okhttp, with okhttp taking care of auth headers and decrypting received data.
so i have a OkHttpClient which i use to get OkHttpUrlLoader. where do i go from here? how can i get glide to use it?
I've found OkHttpGlideModule here https://github.com/bumptech/glide/blob/master/integration/okhttp/src/main/java/com/bumptech/glide/integration/okhttp/OkHttpGlideModule.java but manual or docs on how it can be used. please help

question

Most helpful comment

i'm not sure what exactly you mean when you say that. can you please give
me some reference code?
i'm new with Glide and haven't figured out how it works yet. sorry to keep
asking silly questions.

will something like this work?

Glide.with(context).load(glideUrl).asBitmap().imageDecoder(new ResourceDecoder<InputStream, Bitmap>() {
    @Override
    public Resource<Bitmap> decode(InputStream source, int width, int height) throws IOException {
        BufferedInputStream inputStream = new BufferedInputStream(source);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[4096];
        int count;
        while((count = inputStream.read(buffer)) > -1) {
            outputStream.write(buffer, 0, count);
        }
        byte[] data = outputStream.toByteArray();

        byte[] decryptedData = CryptLib.decode(data);
        Bitmap bitmap = BitmapFactory.decodeByteArray(decryptedData, 0, decryptedData.length);
        BitmapPool pool = ???
        Resource<Bitmap> resource = new BitmapResource(bitmap, pool);
        inputStream.close();
        outputStream.close();
        return resource;
    }

    @Override
    public String getId() {
        return null;
    }
}).into(imageView);

All 20 comments

thank you for swift reply.
i've completed the steps from Integration-Libraries link and Configuration.
how do i use the modules i've created? how can i choose the module to use?

Configuration wiki says:

Finally add a meta-data tag to your AndroidManifest.xml file so Glide can find your module

I think this is the step you're looking for.

After that's done, just do the usual Glide.with... and your module(s) will be initialised on the first such call.

Make sure you don't use @aar in Grade because you have a custom client. Check the merged manifest in build.

is there a way to pass parameters to that module? i need different headers for different urls, so i want to configure okhttp for each image load.
also if i reconfigure okhttp while other image is downloading, will that download fail?

You shouldn't reconfigure. Check out GlideUrl constructor for localised headers. Use LazyHeaders.Builder for dynamic headers.

Thank you very much. That works for me.
One more issue is decrypting image data. The images i load are encrypted with different keys. What is the best way to decrypt data 'on the fly'? Is there a way to intercept byte[] data, decrypt it and then let Glide do the rest?

PS i have the decryptor that has the byte[] decrypt(byte[]) method.

You can try providing an .asBitmap().imageDecoder() which wraps a built-in StreamBitmapDecoder. Just wrap the stream with a decoding stream and pass it on.

If the response headers contain the key you can also try overriding behavior in OkhttpUrlLoader.

i'm not sure what exactly you mean when you say that. can you please give
me some reference code?
i'm new with Glide and haven't figured out how it works yet. sorry to keep
asking silly questions.

will something like this work?

Glide.with(context).load(glideUrl).asBitmap().imageDecoder(new ResourceDecoder<InputStream, Bitmap>() {
    @Override
    public Resource<Bitmap> decode(InputStream source, int width, int height) throws IOException {
        BufferedInputStream inputStream = new BufferedInputStream(source);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[4096];
        int count;
        while((count = inputStream.read(buffer)) > -1) {
            outputStream.write(buffer, 0, count);
        }
        byte[] data = outputStream.toByteArray();

        byte[] decryptedData = CryptLib.decode(data);
        Bitmap bitmap = BitmapFactory.decodeByteArray(decryptedData, 0, decryptedData.length);
        BitmapPool pool = ???
        Resource<Bitmap> resource = new BitmapResource(bitmap, pool);
        inputStream.close();
        outputStream.close();
        return resource;
    }

    @Override
    public String getId() {
        return null;
    }
}).into(imageView);

Yes, something like this.

BitmapPool pool = Glide.get(context).getBitmapPool();
BitmapResource.obtain(bitmap, pool);

The biggest problem is that you're decoding the image yourself losing most of what Glide has to offer. Consider this:

// ... your decryptor until decryptedData
outputStream.close();
// don't close inputStream, because it closes source; that is handled by Glide
source = new ByteArrayInputStream(decryptedData);
return new StreamBitmapDecoder(context).decode(source);

Just noticed your PS, isn't there way to wrap an input stream like you did with buffered, but one which decrypts on the fly? That would prevent from having 2x size of the source image in memory.

source = new DecryptingInputStream(key, source);
return new StreamBitmapDecoder(context).decode(source);

You said they use different keys, I don't see that here.


You would be better off doing this in a subclass of Okhttp:

new OkHttpStreamFetcher() {
    @Override public InputStream loadData(Priority priority) throws Exception {
        InputStream stream = super.loadData(priority);
        return new DecryptingInputStream(stream);
    }
}

That way it's less cumbersome when you call Glide.load. But this only works if the key is static (doesn't vary per stream) or not static, but known before making the request, or returned with the response.

thank you.
i want to use this:

source = new DecryptingInputStream(aesEncryptionKey, iv, source);
return new StreamBitmapDecoder(context).decode(source);

but the problem is that decode() requires width and height which i don't have when i call Glide. is there any way to make Glide do the measuring for me?

You get a width and height in your wrapping ResourceDecoder, pass that on.

Thank you so much!

I finally tested the code and decrypting doesn't work. I have tested my decryptor with the test file, all OK.
Could it be that .asBitmap() modifies the inputStream data before i attempt to decrypt it?

Which interface is the decrypting in? What's your cache strategy?

here's the complete code

       Glide.with(context)
                .load(glideUrl)
                .asBitmap()
                .imageDecoder(new ResourceDecoder<InputStream, Bitmap>() {
                    @Override
                    public Resource<Bitmap> decode(InputStream source, int width, int height) throws IOException {
                        if (aesEncryptionKey.isEmpty()) {
                            return new StreamBitmapDecoder(context).decode(source, width, height);
                        }

                        BufferedInputStream inputStream = new BufferedInputStream(source);
                        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                        byte[] buffer = new byte[4096];
                        int count;
                        while ((count = inputStream.read(buffer)) > -1) {
                            outputStream.write(buffer, 0, count);
                        }
                        byte[] data = outputStream.toByteArray();

                        try {
                            byte[] decryptedData = new CryptLib(256).decrypt(data, aesEncryptionKey, iv);
                            source = new ByteArrayInputStream(decryptedData);
                        } catch (InvalidKeyException e) {
                            e.printStackTrace();
                        } catch (InvalidAlgorithmParameterException e) {
                            e.printStackTrace();
                        } catch (IllegalBlockSizeException e) {
                            e.printStackTrace();
                        } catch (BadPaddingException e) {
                            e.printStackTrace();
                        } catch (NoSuchAlgorithmException e) {
                            e.printStackTrace();
                        } catch (NoSuchPaddingException e) {
                            e.printStackTrace();
                        }
                        outputStream.close();

                        return new StreamBitmapDecoder(context).decode(source, width, height);
                    }

                    @Override
                    public String getId() {
                        return null;
                    }
                })
                .centerCrop()
                .diskCacheStrategy(DiskCacheStrategy.ALL)
                .into(imageView);

It likely fails when it reads the cache, because those use different decoders, try setting sourceDecoder as well: extract this anonymous inner class to DecryptingDecoder and use it like this:

.imageDecoder(new DecryptingDecoder(context, aesEncryptionKey))
.cacheDecoder(new FileToStreamDecoder<Bitmap>(new DecryptingDecoder(context, aesEncryptionKey)))
.diskCacheStrategy(DiskCacheStrategy.SOURCE)

You can't use RESULT cache in this case: #707, you can work around that by adding .encoder(new EncryptingEncoder(context, aesEncryptionKey)) and implementing that class based on BitmapEncoder.

Minor tips:

  • Put a log line in each of these classes to be sure they were invoked.
  • Don't return null from getId, if you must, use ""; but it's better to return the class name so Glide knows it was involved in the load.
  • move new StreamBitmapDecoder(context) to be a field, no need to create a new object every time, because it's stateless

could you please explain the code a little? i can't understand the workflow. thank you.

Thank you very much. Issue solved.
I love Glide support!

I have same problem

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sant527 picture sant527  路  3Comments

StefMa picture StefMa  路  3Comments

ghost picture ghost  路  3Comments

kenneth2008 picture kenneth2008  路  3Comments

billy2271 picture billy2271  路  3Comments