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
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();