Best Practice for Pre-Loading an All-Offline App with Data & 600+ Images

Hello, I’m working on an ionic app that builds PDF reports (using pdfmake).

A report is made up of 1 District, that has 1 to ~7 projects. Each report has a District map and a map for each project. This means I have 1 to 8 map images per district.

I want all of the map images and District names and Project names already on the app when downloaded. Users can then edit / add / remove projects / districts, but since there are about 100 districts and thus 600 projects, I would like the app to start out with this data.

I have built the first version of the app with an initial load of 11 districts with 43 projects and everything works fine. My question is about what is the best way to store these almost 600 map images and preload these districts (names and map images)?

Currently loading my data with an array like below, but doing this for 100 districts doesn’t seem feasible. (Note: I am using base64 for the images because that is what pdfmake takes)

import { District } from '../models/district';
import { Project } from '../models/project';

export const DISTRICTS:District[] = [
  new District(
    "First District Name",
    "data:image/jpeg;base64,/9j/4AAQSkZJRgA... <base64string here. too long to post...",
    [
    new Project("Vintage Detention Pond")
    ]
    ),
  new District(
    "My District 2 Name", 
    "data:image/jpeg;base64,/9j/4AAQSkZJRgA... <base64string here. too long to post...",
    [
      new Project("Detention Pond 1", "data:image/jpeg;base64,/9j/4AAQSkZJRgA... <base64string here. too long to post..."),
      new Project("Detention Pond 2", "data:image/jpeg;base64,/9j/4AAQSkZJRgA... <base64string here. too long to post...")
      ]
    )
  ]

So short story, I’m looking for a good way to store 600 images in my app (that later would need to be converted to bsae64). The app is an offline app so images must be stored in app without having to fetch from a server. Currently using a big array as my storage that the app uses.

Thanks!

You could store everything in your Assets folder, for example. But why do you want it all preloaded? Ionic is moving in the direction of preloading as little as possible – lazy loading pages and components. Why not just load something when it comes into view, especially if it is stored locally?

I think with preloaded the OP means that it doesn’t have to be loaded via HTTP from a server first, right?

Correct. That is what I meant. As in the images do not have to be fetched from an external source.

Is 600 images in my assets/images folder the best way? I guess I would then have to convert to base64 for pdfmake, correct?

That would probably work. Or you could store 600 files already containing the base64 string. Or a json file with an array of 600 base64 strings.

More important: Do you want to sync this from a backend database? Keep the list up to date? Then maybe it makes sense to keep it all in some kind of “database”…

would this just be a .txt file with the base64 string in it that I call as if it’s a .jpg file? Can’t seem to get this to work but would probably be the quickest / easiest way with how everything is coded right now if you had more details on how to do this method

I think this is similar to what I’m doing currently. The downside is that all those base64 strings are long and make sublime text very slow (and that’s with only ~60 base64 images). Any workaround for this?

Currently I am updating the array when changes are made, and then saving using the Ionic Storage. Of course I’m saving the whole array to the key of ‘districts’. I don’t think this is best practice but it saves all the user’s changes locally on their phone as needed. What would be a better way to use a database for this? (and when you say backend database, you mean locally in the app on the phone, correct? No fetching of data from external)

Thanks for the tips so far! You’ve already pointed me in some new areas to try out and look into

If you have pictures, you should probably just put them into the app, then read them on runtime and convert to the format you need them. Only reason not do it this way would be if it is too slow. Then go with any of the “base64” solutions in your app, but make sure you have a simple way to generate them (I would write a simple PHP script that reads all the images in a folder and outputs them as base64 in the format I need - but that is because I do everything in PHP as this is my mother programming language :wink: )

thanks so much for your replies so far! I had a question on where you recommend converting on runtime. my current setup is a file where I just export the array of Districts (as shown in my original post). Could I do something like the below code? (I see lots of convert to base64 code snippets online, but not sure how to use them here as they all require callbacks or promises. So if you have the function I should use, that would be helpful as well)

import { District } from '../models/district';
import { Project } from '../models/project';

//would need a function for this
function convertFileToDataURL(localUrl) {
  //some function to convert local image path and returns base64 string
}

export const DISTRICTS:District[] = [
  new District(
    "HC MU District",
    convertFileToDataURL('assets/img/DistrictMaps/mud-468-map.jpg'), //call the function here like this?
    [
    new Project("Vintage Detention Pond")
    ]
    )
  ];

(this might be a whole new question, so let me know and I can just ask there)

This looks to me like the worst of all possible worlds: it combines all the bloat of inlining the data URLs in main.js with the inefficiency of runtime conversion.

Instead of storing data URLs in the code, I would store only filenames. When an image is actually needed, I would use the File plugin’s readAsDataURL() method to get a data URL for the image. I would not do this at launch time for all 600 images.

1 Like

thanks! this is what I was looking for. Some way to convert the file paths to base64 images. File plugin seems to be the way to do that. Also, only doing it only when necessary will be much better performance wise AND I don’t have to have all those base64 strings in a giant text file that makes using my text editor impossible

…now to just figure out how to use this File plugin since it returns a Promise and it doesn’t seem to be returning in time for when my pdfmake function needs the base64 image. Maybe for another thread :slight_smile:

A Promise is a type of Future, which means at some point it will return data. So you have to explicitly wait for the promise to return data, and you can’t guarantee it will “finish” when you want it to.

So you’d need to do something like:

myPromise.then(base64 => {
  pdfmake.makeMyPDF(base64);
}

To give some more (explicit) help we’d need to see some code though.

2 Likes