This post explains how to fix caching of images from Amazon S3 bucket and similar storage services in React Native using a custom cache key.
Aloha, everyone!
Recently, I was working on a React Native app, whose back-end was relying on Amazon S3 bucket as image hoster.
In order to optimize the performance of the app & reduce the network traffic of the app, it was required to cache images, preferably with some animations.
To accomplish this task, we’ve chosen georstat/react-native-image-cache library.
Setup of this library is pretty simple. After installation, it’s required to create a global config in App.tsx or index.js for image caching, as an example:
import { CacheManager } from '@georstat/react-native-image-cache';
import { Dirs } from 'react-native-file-access';
CacheManager.config = {
baseDir: `${Dirs.CacheDir}/images_cache/`,
blurRadius: 15,
cacheLimit: 0,
sourceAnimationDuration: 500,
thumbnailAnimationDuration: 500,
};
For more options, please, look at the official documentation on GitHub page.
Following usage of image component in the code looks similar to the usage of standard Image component of React Native:
import { CachedImage } from '@georstat/react-native-image-cache';
<CachedImage
source="https://via.placeholder.com/3500x3500"
style={{ height: 350, width: 150 }}
thumbnailSource="https://via.placeholder.com/350x150"
/>;
But here we faced our problem with images from Amazon S3 bucket!
With each app startup, the app was re-downloading images from scratch! Cache doesn’t work.
I started looking for the source of the problem.
Firstly, I looked at the image URL, which I was receiving from the REST API via our back-end.
It had a similar view:
As you can see, we receive an image with credentials and tokens in the URL as params for the access to it — URL params such as X-Amz-Algorigthm, Z-Amz-Credential, X-Amz-Date, X-Amz-Expires, X-Amx-Signature and other.
After that, I searched for the details, what key library is using for images in order to cache them, and I’ve found this code in CacheEntry class:
...
const { exists, path, tmpPath } = await getCacheEntry(source, maxAge);
...
const getCacheEntry = async ( cacheKey: string, maxAge?: number | undefined ): Promise<{ exists: boolean; path: string; tmpPath: string }> => {
…
}
We see that the library is using the whole image URL as a cache key!
And that’s the reason of caching bug with Amazons S3.
The library is re-downloading & caching the same image multiple times, but with different URL (different access token).
It consumes additional disk space & network traffic of the users.
What can we do with that bug?
The back-end couldn’t do anything about that from their side.
Also this problem is actual inother popular image libraries for React Native, such as DylanVann/react-native-fast-image
Solution
So, I’ve decided to make a contribution to the image library to allow setting custom cache keys in the global CacheConfig with getCustomCacheKey function.
So, in the case of Amazon S3 bucket, it’s enough to use as a custom cache key based on the image URL just without any params. Something like that:
https://test-imagedata.s3.eu-west-2.amazonaws.com/uploads/images/filename/614/image.png
In order to achieve this, I’ve added removing URL params logic in getCustomCacheKey function in CacheConfig:
CacheManager.config = {
// ...
getCustomCacheKey: (source: string) => {
// Remove params from the URL for caching images (useful for caching images from Amazons S3 bucket and etc)
let newCacheKey = source;
if (source.includes('?')) {
newCacheKey = source.substring(0, source.lastIndexOf('?'));
}
return newCacheKey;
}
}
And now, it works, the app is caching images from Amazon S3 storage.
This solution could be useful also with other storage services, which put tokens and credentials into the URL of the image.
Also you can add additional checks or regexp in the case, if it shouldn’t influence all images URLs in the app.
Also you can set a custom cache key as you want with getCustomCacheKey function of CacheManager according to your requirements.
Thank you for your attention
Top comments (0)