Downloading Large quantities of Images to internal storage

Hi there

I’m hoping someone can help me out with a problem I’m facing. I’m trying to make an offline app for a sales team, which sell kettles. They have about 800 kettles in their inventory and most of their client interactions will be in an offline environment.

To display the images for the kettles I’m trying to download the images, this means that I’ve got roughly 800 images of the kettles - 1600 if I use thumbnails. I’m using the file Transfer plugin to do this so far but the results I am getting are inconsistent, sometimes it downloads the image, sometimes it doesn’t and sometimes I get a file path but the file doesn’t exist.

This is the code I’m using to get all the downloads

downloadImages( kettles) {
    const downloader$ = [];
    for ( const kettle of kettles ) {
      downloader$.push( this.download( kettle ) );
    }
    console.log( downloader$ );
    return Observable.forkJoin( downloader$ );
  }

  download( kettle: Kettle ) {
    const fullImage$ = new Observable<string>( observer => {
      const fileTransfer: FileTransferObject = this.transfer.create();
      this.file.checkFile( encodeURI( this.file.dataDirectory ), kettle.title + '.' + kettle.image.split( '.' ).pop() ).then( () => {
        observer.next( this.file.dataDirectory + kettle.title + '.' + kettle.image.split( '.' ).pop() );
      }, () => {
        fileTransfer.download( encodeURI( kettle.image ), encodeURI( this.file.dataDirectory + kettle.title + '.' + kettle.image.split( '.' ).pop() ) ).then( ( entry ) => {
          observer.next( entry.toURL() );
        }, () => {
          observer.next( kettle.image );
        } );
      } );
    } );
    const thumbnail$ = new Observable<string>( observer => {
      const fileTransfer: FileTransferObject = this.transfer.create();
      this.file.checkFile( encodeURI( this.file.dataDirectory ), kettle.title + '.' + kettle.thumbnail.split( '.' ).pop() ).then( () => {
        observer.next( this.file.dataDirectory + kettle.title + '.' + kettle.thumbnail.split( '.' ).pop() );
      }, () => {
        fileTransfer.download( encodeURI( kettle.thumbnail ), encodeURI( this.file.dataDirectory + kettle.title + '.' + kettle.thumbnail.split( '.' ).pop() ) ).then( ( entry ) => {
          observer.next( entry.toURL() );
        }, () => {
          observer.next( kettle.thumbnail );
        } );
      } );
    } );

Is there any easier way to go about this, or am I just doing it wrong? I’m using ngrx as well if it makes things different.

1 Like

Some Cordova plugins don’t handle synchrony well. You can’t say “Download these 10 items.” Instead, you have to say, “Download this one. Now this one. Now this one.” If you’re forkJoining 800 requests, you might be overwhelming the plugin.

Is the database 800 images that might change, but probably won’t change very often? One strategy is to ship a set of placeholder images with your app. Say lo-res versions of those 800 images. Then when you display kettle X, you initially show placeholder X, and swap it out for the hi-res image you download from the remote database. You periodically push updates to the placeholder array. Finally you have a generic tea kettle image that is displayed if no placeholder is available for the kettle requested.

Thanks for the response.

The database does change fairly often but there are a select number of core items which won’t so I think you suggestion would be suitable, and have an element to notify the user if the hi res images are downloading in the background.

Thanks for the help, I appreciate it

Why not just store all the images in the assets folder? - that way they will always be available.
It will make the package larger - but that is better than running some download code.

Just update the images in the assets folder and repackage the app.