Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,34 @@ CacheManager.get(uri).cancel(); // Cancel image download if one is in progress
You can also clear the local cache:

```js

import {CacheManager} from "react-native-expo-image-cache";

await CacheManager.clearCache();
```


### Set the cache directory

If you want to work in another cache directory, you can setup the cache directory.

```js
import {FileSystem} from "expo";
import {CacheManager, setBaseDir} from "react-native-expo-image-cache";

const uri = "https://www.google.com/favicon.ico"
setBaseDir(`${FileSystem.cacheDirectory}custom-cache-directoy`);
await CacheManager.get(uri).getPath(); // Cache the image.
```

### Remove an entry from the cache
You can remove a file from cache with it's url like this :

```js
import {CacheManager, removeCacheEntry} from "react-native-expo-image-cache";
const uri = "https://www.google.com/favicon.ico"
// Cache the image & get the images.
const localUri = await CacheManager.get(uri).getPath();
// Destroy the cached image
await removeCacheEntry(uri);

```
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@
"babel-preset-react-native": "^4.0.0",
"crypto-js": "^3.1.9-1",
"eslint-plugin-react-native": "^3.2.1",
"lodash": "^4.17.4"
"lodash": "^4.17.4",
"yarn": "^1.9.4"
},
"peerDependencies": {
"expo": "*",
Expand Down
70 changes: 60 additions & 10 deletions src/CacheManager.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
// @flow
import * as _ from "lodash";
import {FileSystem} from "expo";
import SHA1 from "crypto-js/sha1";
import MD5 from "crypto-js/md5";

const BASE_DIR = `${FileSystem.cacheDirectory}expo-image-cache/`;
let _baseDir = `${FileSystem.cacheDirectory}expo-image-cache/`;
const getBaseDir = (): string => _baseDir;
// TODO maybe some checks are needed, like :
// - will we be able to create the directory if does not exists ?
// - the string is a correct local uri (file://...) ?
export const setBaseDir = (baseDir: string): string => _baseDir = baseDir;

export class CacheEntry {

Expand All @@ -14,14 +19,35 @@ export class CacheEntry {
this.uri = uri;
}

async createBaseDir() {
const BASE_DIR = getBaseDir();
const { exists, isDirectory } = FileSystem.getInfoAsync(BASE_DIR);
if(!exists){
try{
await FileSystem.makeDirectoryAsync(BASE_DIR, {intermediates: true});
}catch(err){
}
}
}

async getPath(): Promise<?string> {
const {uri} = this;
const {path, exists, tmpPath} = await getCacheEntry(uri);
if (exists) {
return path;
}
await FileSystem.downloadAsync(uri, tmpPath);
await FileSystem.moveAsync({ from: tmpPath, to: path });

this.createBaseDir();
try{
await FileSystem.downloadAsync(uri, tmpPath);
}catch(err){
return path;
}
try{
await FileSystem.moveAsync({ from: tmpPath, to: path });
}catch(err){
return path;
}
return path;
}
}
Expand All @@ -38,23 +64,47 @@ export default class CacheManager {
}

static async clearCache(): Promise<void> {
const BASE_DIR = getBaseDir();
await FileSystem.deleteAsync(BASE_DIR, { idempotent: true });
await FileSystem.makeDirectoryAsync(BASE_DIR);
}
}

const getCacheEntry = async (uri: string): Promise<{ exists: boolean, path: string, tmpPath: string }> => {
const getCacheKey = (uri: string): { [key: string]: string, [ext: string]: string } => {
const filename = uri.substring(uri.lastIndexOf("/"), uri.indexOf("?") === -1 ? uri.length : uri.indexOf("?"));
const ext = filename.indexOf(".") === -1 ? ".jpg" : filename.substring(filename.lastIndexOf("."));
const path = `${BASE_DIR}${SHA1(uri)}${ext}`;
const tmpPath = `${BASE_DIR}${SHA1(uri)}-${_.uniqueId()}${ext}`;
return {key: 'I' + MD5(uri), ext};
};

/**
* As we can now set an uri that is not in the cacheDirectory,
* we need to be able to delete files.
*/
export const removeCacheEntry = async (uri: string): Promise => {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

should return be await and Promise be Promise<void>?

const {ext, key} = getCacheKey(uri);
return FileSystem.deleteAsync(
`${getBaseDir()}${key}${ext}`,
{idempotent: true}
);
};

const getCacheEntry = async (uri: string): Promise<{ exists: boolean, path: string, tmpPath: string }> => {
const BASE_DIR = getBaseDir();
const {ext, key} = getCacheKey(uri);
const path = `${BASE_DIR}${key}${ext}`;
const tmpPath = `${BASE_DIR}${key}-${_.uniqueId()}${ext}`;
// TODO: maybe we don't have to do this every time
try {
await FileSystem.makeDirectoryAsync(BASE_DIR);
} catch (e) {
// do nothing
}
const info = await FileSystem.getInfoAsync(path);
const {exists} = info;
return { exists, path, tmpPath };
let info = null;
try{
info = await FileSystem.getInfoAsync(path);
const {exists} = info;
return { exists, path, tmpPath };
}catch(e){
}
return { exists: false, path, tmpPath };
};
2 changes: 1 addition & 1 deletion src/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// @flow
export {default as Image} from "./Image";
export {default as CacheManager} from "./CacheManager";
export {default as CacheManager, setBaseDir, removeCacheEntry} from "./CacheManager";
Loading