Which is the best way to store images for expressjs, mongodb site?
Where do we store images?
Solution # 1 in MongoDB
So the first solution is to store the images inside of MongoDB. It can accommodate image files or any file type. So you can take a file and tie it to a record inside of MongoDB and save it directly to your database.
With this approach tying a description page of a particular article of clothing with its corresponding image becomes easy because you can embed this image directly into your the development of that page and your client would be happy with that approach because when the user retrieves a detailed description page of that article of clothing it comes with the image.
So that's one possible solution, just take the image and store directly into MongoDB.
However, I am going to suggest that this is a bad approach. The reason for that, you can tell your client if there is pushback is that they will usually pay for their Mongo instance in terms of the amount of storage that their Mongo copy uses.
So all the more storage they use up, they pay more money per month.
For example, the last time I checked the use of MLab, they were charging $15 per GB. So that's $15 out of your customer's pocket for hosting 1GB worth of images.
For yet another e-commerce website, we are talking about 3GB easily, which translates into 330 images more or less, equally $15 a month.
So if one of their project managers is uploading a new article of clothing once a day, we are talking about a tremendous cost very quickly.
So, personally I think that storing any kind of file type directly inside of MongoDB is really not an option because it will get very costly.
So that's just one possible solution.
Solution # 2 in HD attached to server
So let's look at a second solution that might be available to you. You might use a hard drive that's tied your Express server. So when this application gets deployed to some cloud environment like Heroku, Digital Ocean, Linode or AWS you usually get a hard drive associated with your application.
So maybe you take the images and place them inside the local hard drive. This approach is what the vast majority of online posts and articles will advocate for:
How to upload, display and save images using node.js and express
https://appdividend.com/2019/02/14/node-express-image-upload-and-resize-tutorial-example/
https://medium.com/@nitinpatel_20236/image-upload-via-nodejs-server-3fe7d3faa642
Just with the three I gathered above you have a pretty robust blueprint to get started with.
Every single one says take the file and save it to your local hard drive. In this particular article:
https://alligator.io/nodejs/uploading-files-multer-express/
They are showing this code:
const storage = multer.diskStorage({
destination: 'some-destination',
filename: function (req, file, callback) {
//..
}
});
They are using the image upload library called multer
which provides the diskStorage()
engine for uploading images to disk.
So this is one approach that the development community at-large agrees with.
This is a good approach in the context of a one-to-one mapping.
The problems with this approach starts to arise when we have multiple machines.
An example of this is if you have multiple machines hosted on Digital Ocean or Linode where each environment is a separate instance.
If you have all your images stored in the accompanying hard drive and then you start to scale your server up, they would each have their own separate hard drive.
So you may have a request that comes in through a load balancer and the load balancer decides where to send the request to like the diagram below:
So the issue with the above architecture is if the image gets saved to one of the two hard drives and then later on a request comes in to access that same image, but imagine the request gets routed to the other Express server with a different hard drive where the image does not exist.
This is a problem that comes up when you start to use a service provider like Linode or Digital Ocean where you have a one-to-one mapping between server and hard drive.
It's a short term solution if that is all you need for now, but once that application starts to scale its going to be a problem.
Solution # 3 outside data store
This third solution is one I have used in the past with React with Node applications and Ruby on Rails applications as well. In fact, my Ruby on Rails portfolio website uses this solution and it sits on the Heroku platform.
So when the image gets uploaded, rather than the Express API trying to store the file locally like on its own hard drive, it will take the image and use an outside data store to hold all the different images from the app.
The one I use for my portfolio website and what I have used for Node with React applications has been Amazon S3, but there also exists Azure File Storage and Google Cloud Storage. These systems are made to hold a tremendous amount of data and they can be any type of file that you can possibly imagine. Not just images such as in your case, but video files, audio files, etcetera.
There is no cap to the amount of storage you can have with S3, but you don't have to use S3, but it is seen as an industry standard right now, but you can easily just as well use Azure and Google Cloud.
The benefit of this solution that I think your customer will appreciate, Amazon S3 charges you like two pennies per gigabyte per month for storage.
I think option 1 is the best choice. However, I strongly suggest using a service like AWS S3 along with cloudfront: this will increase the image load speed and the storage cost is really low.
That said, you can store just the image link or key and then call the URL reference in the front end.
Links of interest:
uppy.io
uppy along with AWS S3