'React-Native <Image source={url}> - Path to Cached Image
Maybe my question will sound foolish, but here it is...
How can we get the path to a cached image (in both iOS and Android)?
Here is my use case: I present a view on my App that lists images from the web --> I get an array of urls from Google Customer Search API based on the user's provided keywords...
<FlatList
style={{ width: '100%' }}
data={this.state.suggestions}
numColumns={2}
renderItem={(img) => {
return (
<TouchableOpacity onPress={() => this.selectImageHandler(img.item)} >
<Image source={{ uri: img.item.link }} />
</TouchableOpacity>
)
}}
keyExtractor={(item, index) => index.toString()}
/>
The result looks like this:
Then the user presses on an image to select it, which then needs to store this image in the app's folder (PictureDir/myappfolder/ in Android, DocumentDir/myappfolder/ in iOS) ...
What I am doing right now is when the image is selected, I download it again:
selectImageHandler = (img) => {
// (... pre code ...)
RNFS.downloadFile({
fromUrl: img.link, // e.g. "http://www.url.to/the/picture.png
toFile: uri, // e.g. "file://"+PictureDir+ "/myAppFolder/picturename.jpg"
}).promise.then( res => {
// (... post code ...)
}
It works fine! But it takes a bit of time, as it downloads again the image, but I feel this is doing it twice, as it was downloaded already a stored in the cache to be displayed.
So here comes my question again, is there a way to know where the image was stored in the cache, so that when the user pressed the image to save it, it will not download it again, but will rather move it from the cache folder to the app's folder?
Am I making any sense? Or is redownloading the right approach?
Thanks for your help!
Solution 1:[1]
One way to avoid re-downloading images a few times might be to take over the control of downloading from a remote url from the <Image> component. Basically, you can download the remote image using the RNFS.downloadFile method and then supply the local URI (toFile value) as the image source. This requires a bit more work, of course, as we need to create a wrapper component, but this approach also provides options to control the display of an image while it's loading.
For example:
import React, { useState, useLayoutEffect } from 'react';
import RNFS from 'react-native-fs';
import { URL } from 'react-native-url-polyfill';
const CACHE_DIR = RNFS.DocumentDirectoryPath; // Cross-platform directory
function ImageCard ({ imgUrl }) {
const [cachedImgUrl, setCachedImgUrl] = useState(null);
const [isImageLoading, setIsImageLoading] = useState(true);
useLayoutEffect(() => {
const getCachedImageUrl = async () => {
try {
const basename = new URL(imgUrl).pathname.split('/').pop();
const localImgUrl = `file://${CACHE_DIR}/${basename}`;
if (await RNFS.exists(localCacheUrl)) {
setCachedImgUrl(localImgUrl);
} else {
const download = RNFS.downloadFile({
fromUrl: imgUrl,
toFile: localImgUrl,
});
const downloadResult = await download.promise;
if (downloadResult.status === 200) {
setCachedImgUrl(localImgUrl);
}
}
} catch (err) {
// handle error
} finally {
setIsImageLoading(false);
}
};
getCachedImageUrl();
}, [imgUrl]);
if (isImageLoading || !cachedImgUrl) {
// A better idea would be to return some `<Loader />` component, or some placeholder, like skeleton animation, etc. This is just an example.
return null;
}
return (
<Image source={{ uri: localImgUrl }} />;
);
}
The <ImageCard /> component replaces the plain <Image /> component in the <FlatList /> and downloads from the remote image URL only once.
The code above is simplified and it assumes that you have unique image names that you can use as the identifiers on the file system, and that the image urls don't include any search parameters, etc. Please be cautious and adapt the code for your needs before using it directly.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |

