Server-side uploading of the image in MEAN Stack
In our previous section, we successfully implemented our mime type validator and used it for our image validation. In this section, we will implement the server-side file upload functionality.
Uploading a file means on the server, we need to extract such a file byte body from our incoming requests, and we can't do that with bodyParser. The bodyParser works for JSON or URL encoded data but not for files. We will do the following steps to perform the server-side uploading:
1) We need to install the "multer" package. This is a package for NodeJS and ExpressJS, which allows us to extract incoming files. We will install the multer package using the npm in the following way:
2) After installing the package, we can use it. We will use multer by simply attaching it to certain routes that should be able to accept files. First of all, we need to import the multer in our post.js file in the following way:
3) Multer needs some configuration, so we will create a new constant, and in this constant, we need to define where multer should put files that it detects in the incoming requests. We will do this by calling the diskStorage() method of multer in the following way:
5) In this object, we set two keys, i.e., destination and filename. The destination key is a function that will be executed whenever multer tries to save a file. This function takes a request object, the file which is extracted and a callback.
6) We need to call that callback to pass back where should we store the information to multer. We will call the callback, and to that callback, we need to pass two things or two arguments. The first argument is whether we detected some error, and for that, we pass null and the second argument is simply a string with a path to the folder where this should be stored.
We need to create the images sub-folder in the backend folder.
7) The filename key is another function where we also get the request object, the file and a callback. In this function, we will extract the name. We will get the name using the originalname property of the file. We will also need to normalize this a bit, and for doing this, we will convert it to all lowercase, and then we will split it by whitespace and join it again with a dash. By doing that, any whitespace in the filename will be replaced with a dash.
8) There is a problem, and that is, this will miss the file extension. We will extract this too, though, and we can get the file mime-type to be precise. The multer gives us the mime-type. We will create a helper constant to have a map of mime types and which extension that we want. We need to support three types of files, i.e., for image/png as a mime type, we will use png as an extension, for image.jpeg as a mime type, we will use jpg as an extension, and for image/jpg as a mime type, we will use jpg as an extension in the following way:
9) Now, we will get the extension, and we will do this by creating a new constant in the filename function. We will use the MIME_TYPE_MAP constant, and in there, we pass the "file.mimetype" property like:
10) After that, we will call a callback to pass that information to the multer. We don't have an error; that's why we pass null as a first argument, and we will construct our name as the second argument, and we will construct it by using the name in the following way:
This callback function should construct a unique filename based on the input file name, the current date and the correct extension.
11) As an extra security layer, in the destination, we have to throw and return an error if we detect that we don't have one of these mime types. We will do this by creating a new constant, i.e., isValid, and we will use MIME_TYPE_MAP, and in there we pass the "file.mimetype" like as:
Here, MIME_TYPE_MAP[file.mimetype] will return nothing, undefined, or null if we get a mime type that is not part of MIME_TYPE_MAP.
12) So, we will then create a new error using the new keyword and the Error() method. We will pass the message in the Error function and check for isValid constant. If it is not null or undefined, we will set error equals null. Otherwise, we leave the error message. In the callback, we pass the error instead of null as a first argument like:
We configured multer above. Now, it knows where to store things and how to store them.
13) Now, it's time to use the multer. We will use it in the post route by passing it as an extra middleware. In the post route, we will pass the path first and then we will pass another function that will do something to the request before our function run. We want to use the multer() function in which we pass our configuration, i.e., storage, and then we will call the single() here. And pass the image as a string in the function. Using a single() method means that we are expecting a single file.
This simply means multer will now try to extract a single file from the incoming request and will try to find it on an image property in the request body.
That's the backend setup, and in order to be able to test it, we need to add the upload functionality in the Angular code. We will do this in the next section.