Home > Software design >  what is the best approach to retrieve images from MongoDB?
what is the best approach to retrieve images from MongoDB?

Time:03-17

I have a web application that stores and retrieves images from MongoDB using GridFS. My issue is that when the user makes a request that prompts the server to retrieve images from the database, it takes a very long time.

For instance, a request that retrieves only two documents in the database and then loops through each document to get the images associated with each document can take up to 45 seconds. In this case, the total amount of images retrieved from the database is around 10.

Here is my approach:

// 1. Query the database 
var users = await User.find({ 'key’': value });

// 5. Use GridFs to get the image and return the result 
const createStream = (filename) => {

        return new Promise((resolve, reject) => {
            let readstream = gfs.createReadStream(filename);
            let fileChunks = [];

            readstream.on('data', function (chunk) {
                fileChunks.push(chunk);
            });

            readstream.on('end', function () {
                let concatFile = Buffer.concat(fileChunks);
                let imageFormated = Buffer(concatFile).toString("base64");
                resolve(imageFormated)
            });
       })
}




// 4. Iterate though each of the images info and retrieve the file name so it can be used with gfs.createReadStream( );
 const iterateImages = async (currentPlaceImages) => {

        let imagesFormattedArray = [];

        for (var i = 0; i < currentPlaceImages.length; i  ) {

            let filename = currentPlaceImages[i].filename;

            // console.log(filename);

            let imageFormated = await createStream(filename);
            // console.log(imageFormated);
            imagesFormattedArray.push(imageFormated)
            if (i === currentPlaceImages.length - 1) {
                return imagesFormattedArray
            }

        }

 }


// 3. favPlaces represents  a field-value pair of each document that  contains an object of ‘favorite places’, each of which with a ‘placeImage’ array of all the images associated with a place

const getImagesOfPlace = async (favPlaces) => {

        let imagesStreamArray = [];

        for (var w = 0; w < favPlaces.length; w  ) {.

            let currentPlaceImages = favPlaces[w]['placeImage'];
            let imagesStream = await iterateImages(currentPlaceImages);
            imagesStreamArray.push(imagesStream);

            if (w === favPlaces.length - 1) {

                return imagesStreamArray;
            }

        };
   }

// 2. Loop through the documents  retrieved from the database and access the field - value pair that contains the information of the images that I want to retrieve using GridFs 
let arrayOfUsers = [];

const getImages = async () => {

        let arrayOfFormattedImages = [];

        for (var i = 0; i < users.length; i  ) {

                    let favPlaces = users[i]['favoritePlaces'];
                    let stream = await getImagesOfPlace(favPlaces);

                   userObj['imageFormatted'] = stream;
                   arrayOfUsers.push(userObj);
        }
};

await getImages();

Here is a visual representation of the flow enter image description here

As mentioned, this approach takes very long to get a server response. If any one knows a better, more effective and fast way of doing this, please let me know.

Thanks!

CodePudding user response:

Because you use for loops the images will load one after the other instead of all at once, the loop will iterate then await then iterate again and await and so on.... You need to fire all async actions at once and await for all of them to finish, Promise.all using Array.map is a way to do it. Try something like this:

const iterateImages = (currentPlaceImages) => {
  return Promise.all(
    currentPlaceImages.map(async (i) => {
      const filename = i.filename;
      return createStream(filename);
    })
  );
};

const getImagesOfPlace = async (favPlaces) => {
  return Promise.all(
    favPlaces.map(async (p) => {
      const currentPlaceImages = p.placeImage;
      return iterateImages(currentPlaceImages);
    })
  );
};

const getImages = async () => {
  return Promise.all(
    users.map(async (u) => {
      const favPlaces = u.favoritePlaces;
      const stream = await getImagesOfPlace(favPlaces);
      u.imageFormatted = stream;
      return u
    })
  );
};

let arrayOfUsers = await getImages();
  • Related