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

Allow changing the cache root directory #38

Merged
merged 4 commits into from
Jul 19, 2017
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions CachedImage.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,17 @@ const CachedImage = React.createClass({
React.PropTypes.bool,
React.PropTypes.array
]).isRequired,
resolveHeaders: React.PropTypes.func
resolveHeaders: React.PropTypes.func,
cacheLocation: React.PropTypes.oneOf(Object.values(ImageCacheProvider.LOCATION)).isRequired
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd consider making this a string value instead of an enum to allow more flexibility.
We could expose the LOCATION enum with some convenience values, but still allow the user to input what ever he wants
What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I concur with this. A future change I would like to add is to support falling back to multiple cache locations.

One of our applications would be bundling a set of content to the app on for release. We'd like to directly reference the bundled cached files if avaliable (rather than seeding by duplicating or pulling a fresh cache down).

By accepting cacheLocation as string, this may be one step closer to our requirement.

},

getDefaultProps() {
return {
renderImage: props => (<Image ref={CACHED_IMAGE_REF} {...props}/>),
activityIndicatorProps: {},
useQueryParamsInCacheKey: false,
resolveHeaders: () => Promise.resolve({})
resolveHeaders: () => Promise.resolve({}),
cacheLocation: ImageCacheProvider.LOCATION.CACHE
};
},

Expand Down Expand Up @@ -116,7 +118,9 @@ const CachedImage = React.createClass({
processSource(source) {
const url = _.get(source, ['uri'], null);
if (ImageCacheProvider.isCacheable(url)) {
const options = _.pick(this.props, ['useQueryParamsInCacheKey', 'cacheGroup']);
let options = _.pick(this.props, ['useQueryParamsInCacheKey', 'cacheGroup']);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the change to let?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No reason, will revert to const. I think I missed that during cleanup.

options.cacheLocation = this.props.cacheLocation;

// try to get the image path from cache
ImageCacheProvider.getCachedImagePath(url, options)
// try to put the image in cache if
Expand Down
36 changes: 27 additions & 9 deletions ImageCacheProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ const {
} = RNFetchBlob;

const baseCacheDir = fs.dirs.CacheDir + '/imagesCacheDir';
const baseBundleDir = fs.dirs.MainBundleDir + '/imagesCacheDir';

const LOCATION = {
CACHE: 'cache',
BUNDLE: 'bundle'
};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The LOCATION enum still contains some defaults / convenience values.


const SHA1 = require("crypto-js/sha1");
const URL = require('url-parse');
Expand All @@ -18,7 +24,8 @@ const defaultImageTypes = ['png', 'jpeg', 'jpg', 'gif', 'bmp', 'tiff', 'tif'];
const defaultResolveHeaders = _.constant(defaultHeaders);

const defaultOptions = {
useQueryParamsInCacheKey: false
useQueryParamsInCacheKey: false,
cacheLocation: LOCATION.CACHE
};

const activeDownloads = {};
Expand Down Expand Up @@ -58,6 +65,14 @@ function generateCacheKey(url, options) {
return SHA1(cacheable) + '.' + type;
}

function getBaseDir(cacheLocation) {
switch (cacheLocation) {
case LOCATION.CACHE: return baseCacheDir;
case LOCATION.BUNDLE: return baseBundleDir;
default: return baseCacheDir;
}
}

function getCachePath(url, options) {
if (options.cacheGroup) {
return options.cacheGroup;
Expand All @@ -72,7 +87,7 @@ function getCachedImageFilePath(url, options) {
const cachePath = getCachePath(url, options);
const cacheKey = generateCacheKey(url, options);

return `${baseCacheDir}/${cachePath}/${cacheKey}`;
return `${getBaseDir(options.cacheLocation)}/${cachePath}/${cacheKey}`;
}

function deleteFile(filePath) {
Expand Down Expand Up @@ -300,23 +315,25 @@ function seedCache(local, url, options = defaultOptions) {

/**
* Clear the entire cache.
* @param cacheLocation
* @returns {Promise}
*/
function clearCache() {
return fs.unlink(baseCacheDir)
function clearCache(cacheLocation) {
return fs.unlink(getBaseDir(cacheLocation))
.catch(() => {
// swallow exceptions if path doesn't exist
})
.then(() => ensurePath(baseCacheDir));
.then(() => ensurePath(getBaseDir(cacheLocation)));
}

/**
* Return info about the cache, list of files and the total size of the cache.
* @param cacheLocation
* @returns {Promise.<{size}>}
*/
function getCacheInfo() {
return ensurePath(baseCacheDir)
.then(() => collectFilesInfo(baseCacheDir))
function getCacheInfo(cacheLocation) {
return ensurePath(getBaseDir(cacheLocation))
.then(() => collectFilesInfo(getBaseDir(cacheLocation)))
.then(cache => {
const files = _.flattenDeep(cache);
const size = _.sumBy(files, 'size');
Expand All @@ -336,5 +353,6 @@ module.exports = {
deleteMultipleCachedImages,
clearCache,
seedCache,
getCacheInfo
getCacheInfo,
LOCATION
};
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ When providing `source={{uri: 'https://example.com/path/to/remote/image.jpg'}}`
* `useQueryParamsInCacheKey` - _array|bool_ an array of keys to use from the `source.uri` query string or a bool value stating whether to use the entire query string or not. **(default: false)**
* `defaultSource` - prop to display a background image while the source image is downloaded. This will work even in android, but will not display background image if there you set borderRadius on this component style prop
* `resolveHeaders` - _function_ when provided, the returned object will be used as the headers object when sending the request to download the image. **(default: () => Promise.resolve({}))**
* `cacheLocation` - _'cache'|'bundle'_ allows changing the root directory to use for caching. The default `'cache'` dir is sufficient for most use-cases. Images in this directory may be purged by Android automatically to free up space. Use `'bundle'` if the cached images are critical (you will have to manage cleanup manually). **(default: 'cache')**

### ImageCacheProvider
`ImageCacheProvider` exposes interaction with the cache layer that is used by `CachedImage` so you can use it to prefetch some urls in the background while you app is starting,
Expand Down Expand Up @@ -78,9 +79,10 @@ ImageCacheProvider.deleteMultipleCachedImages([

#### `type: CacheOptions`
```
type ReadDirItem = {
type CacheOptions = {
useQueryParamsInCacheKey: string[]|bool; // same as the CachedImage props
cacheGroup: string; // the directory to save cached images in, defaults to the url hostname
cacheLocation: 'cache'|'bundle'; // the root directory to use for caching, corresponds to CachedImage prop of same name, defaults to 'cache'
};
```

Expand Down