Skip to content

Commit

Permalink
fix(image-loader): use tempDirectory on iOS (#39)
Browse files Browse the repository at this point in the history
* fix(image-loader): use tempDirectory on iOS

The tempDirectory can be accessed from the WKWebView as well; no need
for base64!

* Revert "fix(image-loader): use tempDirectory on iOS"

This reverts commit bea5839.

* Specify the imageReturnType (base64/uri)

If 'uri' is used the files will be copied to the temporary directory if
the WKWebView engine is used. Files in this directory are not
persistent, but accessible from within the WebView.
If the files do not exist any longer they will automatically be copied
again!
  • Loading branch information
swiftyone authored and ihadeed committed Apr 18, 2017
1 parent ccf3ccc commit f29d630
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 26 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- Allows setting **maximum cache age** to delete old images automatically. This is optional and **disabled by default**.
- Allows setting **maximum cache size** to control how much space your app takes out of the users' phones. This is optional and **disabled by default**.
- Allows setting a **fallback image** to be displayed in case the image you're trying to show doesn't exist on the web. (optional)
- Works with the **[WKWebView Engine](https://github.com/apache/cordova-plugin-wkwebview-engine)** (iOS), as the images are copied to the temporary directory, which is accessible form within the WebView

![Gif](https://github.com/ihadeed/ionic-image-loader-example/blob/master/gif.gif?raw=true)

Expand Down Expand Up @@ -250,6 +251,14 @@ Example:
this.imageLoaderConfig.setMaximumCacheAge(7 * 24 * 60 * 60 * 1000); // 7 days
```
---
#### setImageReturnType(imageReturnType: string)
Set the return type of cached images. By default this option is set to 'uri', which will return the native file URL. If you want to get a base64-encoded representation of the file set the return type to 'base64'.
Example:
```typescript
this.imageLoaderConfig.setImageReturnType('base64');
```
---
# Preloading images
```typescript
Expand Down
10 changes: 10 additions & 0 deletions src/providers/image-loader-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export class ImageLoaderConfig {

maxCacheAge: number = -1;

imageReturnType: string = 'uri';

private _cacheDirectoryName: string = 'image-loader-cache';

set cacheDirectoryName(name: string) {
Expand Down Expand Up @@ -142,4 +144,12 @@ export class ImageLoaderConfig {
this.maxCacheAge = cacheAge;
}

/**
* Set the return type of cached images
* @param imageReturnType {string} The return type; either 'base64' or 'uri'
*/
setImageReturnType(imageReturnType: string): void {
this.imageReturnType = imageReturnType;
}

}
120 changes: 94 additions & 26 deletions src/providers/image-loader.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { File, FileEntry, FileReader, FileError } from '@ionic-native/file';
import { File, FileEntry, FileError, DirectoryEntry } from '@ionic-native/file';
import { Transfer } from '@ionic-native/transfer';
import { ImageLoaderConfig } from "./image-loader-config";
import { Platform } from 'ionic-angular';
Expand Down Expand Up @@ -108,7 +108,23 @@ export class ImageLoader {

this.file.removeRecursively(this.file.cacheDirectory, this.config.cacheDirectoryName)
.then(() => {
this.initCache(true);
if (this.isWKWebView) {

// also clear the temp files
this.file.removeRecursively(this.file.tempDirectory, this.config.cacheDirectoryName)
.catch((error) => {
// Noop catch. Removing the tempDirectory might fail,
// as it is not persistent.
})
.then(() => {
this.initCache(true);
});

} else {

this.initCache(true);

}
})
.catch(this.throwError.bind(this));

Expand Down Expand Up @@ -284,7 +300,7 @@ export class ImageLoader {
&& (Date.now() - metadata.modificationTime.getTime()) > this.config.maxCacheAge
) {
// file age exceeds maximum cache age
return this.file.removeFile(this.file.cacheDirectory + this.config.cacheDirectoryName, file.name);
return this.removeFile(file.name);
} else {

// file age doesn't exceed maximum cache age, or maximum cache age isn't set
Expand Down Expand Up @@ -352,7 +368,7 @@ export class ImageLoader {
if (typeof file == 'undefined') return maintain();

// delete the file then process next file if necessary
this.file.removeFile(this.file.cacheDirectory + this.config.cacheDirectoryName, file.name)
this.removeFile(file.name)
.then(() => next())
.catch(() => next()); // ignore errors, nothing we can do about it
}
Expand All @@ -364,6 +380,24 @@ export class ImageLoader {

}

/**
* Remove a file
* @param file {string} The name of the file to remove
*/
private removeFile(file: string): Promise<any> {
return this.file
.removeFile(this.file.cacheDirectory + this.config.cacheDirectoryName, file)
.then(() => {
if (this.isWKWebView) {
return this.file
.removeFile(this.file.tempDirectory + this.config.cacheDirectoryName, file)
.catch(() => {
// Noop catch. Removing the files from tempDirectory might fail, as it is not persistent.
});
}
});
}

/**
* Get the local path of a previously cached image if exists
* @param url {string} The remote URL of the image
Expand All @@ -381,32 +415,58 @@ export class ImageLoader {
const fileName = this.createFileName(url);

// get full path
const dirPath = this.file.cacheDirectory + this.config.cacheDirectoryName;
const dirPath = this.file.cacheDirectory + this.config.cacheDirectoryName,
tempDirPath = this.file.tempDirectory + this.config.cacheDirectoryName;

// check if exists
this.file.resolveLocalFilesystemUrl(dirPath + '/' + fileName)
.then((fileEntry: FileEntry) => {
// file exists in cache

// now check if iOS device & using WKWebView Engine
if (this.isWKWebView) {

// Read FileEntry and return as data url
fileEntry.file((file: any) => {
const reader = new FileReader();

reader.onloadend = function() {
resolve(this.result);
};

reader.readAsDataURL(file);
}, reject);

} else {

// return native path
resolve(fileEntry.nativeURL);

if (this.config.imageReturnType === 'base64') {

// read the file as data url and return the base64 string.
// should always be successful as the existence of the file
// is alreay ensured
this.file
.readAsDataURL(dirPath, fileName)
.then((base64: string) => {
resolve(base64);
})
.catch(reject);

} else if (this.config.imageReturnType === 'uri') {

// now check if iOS device & using WKWebView Engine.
// in this case only the tempDirectory is accessible,
// therefore the file needs to be copied into that directory first!
if (this.isWKWebView) {

// check if file already exists in temp directory
this.file.resolveLocalFilesystemUrl(tempDirPath + '/' + fileName)
.then((tempFileEntry: FileEntry) => {
// file exists in temp directory
// return native path
resolve(tempFileEntry.nativeURL);
})
.catch(() => {
// file does not yet exist in the temp directory.
// copy it!
this.file.copyFile(dirPath, fileName, tempDirPath, fileName)
.then((tempFileEntry: FileEntry) => {
// now the file exists in the temp directory
// return native path
resolve(tempFileEntry.nativeURL);
})
.catch(reject);
});

} else {

// return native path
resolve(fileEntry.nativeURL);

}
}
})
.catch(reject); // file doesn't exist
Expand Down Expand Up @@ -441,7 +501,11 @@ export class ImageLoader {
* @returns {Promise<boolean|FileError>} Returns a promise that resolves if exists, and rejects if it doesn't
*/
private get cacheDirectoryExists(): Promise<boolean> {
return this.file.checkDir(this.file.cacheDirectory, this.config.cacheDirectoryName);
return this.file
.checkDir(this.file.cacheDirectory, this.config.cacheDirectoryName)
.then(() => {
return (this.isWKWebView ? this.file.checkDir(this.file.tempDirectory, this.config.cacheDirectoryName) : Promise.resolve());
});
}

/**
Expand All @@ -450,7 +514,11 @@ export class ImageLoader {
* @returns {Promise<DirectoryEntry|FileError>} Returns a promise that resolves if the directory was created, and rejects on error
*/
private createCacheDirectory(replace: boolean = false): Promise<any> {
return this.file.createDir(this.file.cacheDirectory, this.config.cacheDirectoryName, replace);
return this.file
.createDir(this.file.cacheDirectory, this.config.cacheDirectoryName, replace)
.then(() => {
return this.isWKWebView ? this.file.createDir(this.file.tempDirectory, this.config.cacheDirectoryName, replace) : Promise.resolve();
});
}

/**
Expand Down

0 comments on commit f29d630

Please sign in to comment.