Uploading an Image | Creating a REST API with Node.js

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
welcome back to this series where we're building a restful api with node.js we added so many features now i want to add another crucial one uploading files to be precise uploading images here and of course i will show you the server side of it how can we accept and store files that is what i want to do in this video so let's start with it so let's implement file uploading and let's say we want to do this for products so in the product routes here for posting a new product it might make sense to be able to accept an image a product image which we then store on our server and what we then also store an entry in the database with the location of that stored image so that we can send it back with a get request so like when we get all um products so that we have that data on the client available too so the first step is to accept images here in this post route and now there are two different approaches you could take here the first approach is that you build an additional endpoint where you accept binary data only so where you don't try to get request body because request body as we use it here won't be available because the binary body will not be parsed by our body parser package since that only parses url encoded or json bodies instead we could read out the raw request body and you will find a link in the video description where you can see this approach in action and how you would parse that incoming data one problem we have with that approach is that we also somehow need to find out to which product this file belongs and certainly there are ways to pass that information along with the file itself but an easier approach is to change the way this post route here works right now we're getting request body here which is coming from our body parser plugin which parses the incoming json body now if we accept a different kind of body namely forum data to be precise multi-part foreign data then this changes then we can use this forum data object javascript offers us or you automatically get when you submit a forum to submit both fields where we have data like names or email addresses and files where we have well the file and this is the approach i want to use here now to be able to read such incoming requests which hold form data data which is just a different content type i need a separate package so i will install it with npm install dash dash save and the name of the package is malter now malter is simply a package that like body parser is able to parse incoming bodies but here we're talking about form data bodies so it's essentially an alternative to body parser for well bodies that can't be handled by body parsers which is the case for foreign data thereafter i'll restart my server and now we can start implementing malter you can find detailed instructions about how to use malta in the video description it's actually not that difficult though in the products.js file to where i want to get a file i will create a new constant which i'll name malter where i require well malter so the package we just installed thereafter i'll create a new constant upload where i execute malter like this this will basically initialize it now we can pass a configuration to malta here and i will do so i will pass a configuration which is destination uploads for example now this specifies a folder where malter will try to store all incoming files this folder of course isn't publicly accessible by default so you will probably have to turn it to a static folder so what we can do is we can go to our app.js where we initialize our whole app our express app and set this folder up to be statically accessible we'll do this in a second step though for now let's stick to implementing the upload functionality and see if that works as it should so we initialized multi and we're saying hey store all files in this place now we want to use it on this post route here how do we do that you actually can pass an argument prior to the function where we handle the incoming request you can pass as many handlers as you want here and each handler is just a middleware that is executed before the next one runs and turns out that this upload object we created by initializing multer here gives us a couple of middlewares we can put in front of our main handler whoops we are here so here we can execute upload and then there are a couple of methods for us single is interesting single means that here we will get one file only so it will only try to parse one file and by just passing upload single here in front of this handler it will parse that file you here also need to specify the name of the field that will hold the file i'll name it product image here like this now with that we should be able to use that file in here and let's start simply by simply outputting it so console.log and now you can access request file this is a new object that will be available to you due to this middleware being executed first now let's save this and i'm getting an error we get a permission problem here with the uploads folder the reason for this is a typo here where i set up the destination uploads the leading slash turns this into an absolute path so now it would try to create this in my root node modules folder which i of course don't want so let's now restart npm start and now it's working so turn this into a relative path by not specifying a leading slash here with that let's simply try it out let's post a new product here let's go to body and now this will fail i can already prepare you for that because we'll now also still try to parse our json data which won't be available but still let's see if we at least get that image up there so i will switch that to form data here the body of the post request and now we basically ignore the body down there the json body we just add key value pairs here so what we can try is that we will also pass name and price maybe request body still works because malter handles this let's see so we will have name and we will have a price key here the price could be 12.99 and the name could be harry potter 5 and then we have an additional key which is of type file here which should be named product image because that's what we're trying to extract here and now i can choose a file now i prepared a fitting file here for christmas let's open that and now let's try sending this and now i get an unexpected token in json position 0. now it makes sense because in the headers i set the content type to application json so let's remove that here and let's try sending this again now we get message not found the reason for this is because it should be slash products here little mistake on my site so let's try again and now i created a product successfully so no failure and we also get an uploads folder with a file in there with some cryptic file name which if you were to open it in your explorer or finder will actually be the image you store it the file name and file extension is missing but we'll take care about this later but the file is getting stored and that's pretty huge you also saw that unlike expected it didn't crash because that's the second thing malta does it doesn't just give you request file it also gives you a request body and since we passed name and price with the request this works and if you're wondering why we didn't have to set the header well this is set automatically here when we choose form data as a body then the header sent was actually the right one to be parsed by malter you also see that we locked information about the file here on the backend due to this line here and there you see it got the field name it also found the file name the encoding the mime type and where it stored it the file name it created here and therefore the path where it uploaded it here and that of course is all pretty great now let's use that knowledge here that we have all the information in the file to change the way it is stored right now we're storing this cryptic name here in uploads we can also change the configuration up here when we initialize multer though we can be more detailed here and define how we want to store that file we can also make sure that we only store certain types of files so let's implement both let me first implement a storage strategy for that i'll add a new constant which i'll name storage and there i will use the multiple package and they're the disk storage function you can pass a javascript object to it to configure it and this basically allows you to adjust how files gets stored it's a therefore more detailed way of storing files than just setting up desk there you have to provide two properties a destination property which is a function that defines where the incoming file should be stored and you get access to the full request in this function to the file into a callback so all of that is passed into the function automatically by malter and we also have a file name function here which defines how the file well should be named you get a request the file and a callback here too malter will execute these functions whenever a new file is received in the destination you execute the callback in the end and to it you pass a potential error here now because we've gotten on and then the path where you want to store the file and this could be a relative path where we just again use uploads so the file we already specified before now in the file name function you define how the file should be named and there you could choose file dot file name we saw in the log that this property is available or you choose the original name if you want to if you want to keep this you could also do something like get new date to iso string to get the current date in the iso string form and concatenate this uh with the original name something like that original name whoops all lowercase well let's see if that works as expected we now have to pass this to malta here so now in this object we pass to malta we set storage to our storage constant which holds our own strategy then we save this and then let's send the same request again maybe with harry potter 6 so that we can see a difference if we do send this let's have a look at the uploads folder now we see we got the file named with the date and thereafter also with the original file name and we can see the image here because it has the right extension now too let me delete the other file therefore so this is how we can fine-tune multi when it comes to this now you might also be interested in filtering out certain files for one you can set some limits to for example not accept files that are bigger than a certain size you do this by passing the limits property to that configuration object you pass to multur and there you can set file size whoops that first of all takes another object and then in that object you can set file size to a number in bytes so you could choose 1024x 1024 so now you would have one megabyte here maybe times five to only accept files up to five megabytes now that is one additional check you can implement but maybe you need a more precise filter to accept only certain mime types for that you can also set up your own filter so i'll create a new constant which i'll name file filter now that is a function where you get a request the file and a callback here i'll write it as an arrow function you could use it as a normal function with the function keyword too and in there you can either reject or accept an incoming file you reject a file by passing callback null false this will simply ignore the file will not store it you accept it by executing callback null true this will store the file or you can also throw an error so if you don't set null it will return with an error ignoring it or not saving it with false does not throw an error it just doesn't save the file now what you can do is you can access some file information here to filter out certain files you don't want for example we can add a if check here where we say if file and then there is a mime type property automatically populated by malta you could see this in the log earlier here mime type so if filemind type is image slash jpg or if the file mime type is image slash png then let's say you want to store it so then you'll take that callback where you pass true as the second argument this will store the file else so for all other incoming mime types like zip files but also gives and so on you could execute callback false to not save it now with that let's see that in action and to see it we have to pass it to our mulcher options so here where we have storage and limits i'll now add an additional property so we have storage we have limits here let's now add one additional property which is file filter and i'll set this equal to file filter to my constant now if i save this and i send this request again i get a success message and it does store the file because i accept jpeg files and that's what i'm sending if i temporarily remove that though and save everything now if you're safe to say send the same request it succeeds here but the file isn't stored it's right that it succeeds because i don't throw an error i could do this here i could return a new error here with some message so i could do that and not here but in the second second callback so down there but i actually don't want to do that i just want to not store the file let's say and of course the exact behavior is up to you now i'll re-add image here image jpeg as a valid mime type and now sending this again will also store it again so this is now storing the files correctly now what about getting the files now we store the files on our backend here that's nice but we also want to store an entry in the database so that when we get a list of all the files we actually can can see them there so we can access them for that let's work on our model on the product model besides name and price we also need a product image field or something like that the type will just be a string because it's just a url and you might also set required to true that's up to you if you want to make an image required or not so if i save this and i go back to products when we store a product in the post route we now also need to set up this product image property now so this one which should hold a url to the image now how do we get that image url well we can get it from malter if we have a look at our console and we scroll up to the point of time when we logged everything you see we had a path property here and that is what we're interested in so what i will send here or what i what i store here is actually my pop so file path and with that oh this should be request file here so request file will be default of so with that we're getting that we're storing that information now when we retrieve files here for example when we get all files we just retrieve the name the price and the id now we should also add product image here to make sure we get this too and the same for the route where we get a single product here we also should retrieve that from the database now we're not done yet though just adding this here doesn't do the trick here for a single product we do output this here so that is okay but for all the products selecting all the fields is one step but then here we return our own object anyways so there we should also return a product image which should be dock product image now if we save this and we send another post request to products with our file the file gets stored if we now get all products we will see that if we scroll through the list of products here we get some with a product image because i tested this between cuts a little bit now if i pick for example the last one here and i copy that product image and i go to the browser and add that path after localhost 3000 i'm getting an error a not found error so this isn't found this route isn't found and it makes sense because there is no such route in our project we got routes for posting and getting getting our products but we have no route to handle requests at a slash uploads url and by default that folder isn't publicly accessible either so this all doesn't work now there are two possible approaches you can take here you can either implement a route for example here in the products routing file where you parse all requests coming to slash uploads and where you then look up the image file name and see if you got such an image and return it manually or you make the uploads folder publicly available that's what i'll do and i'll do this in app.js for this i'll add a new middleware here right at the start and it's a middleware shipping with express the static function here is the one i'm looking for it makes a folder statically so publicly available here i can pass uploads and this will make the uploads folder available to everyone if we now save this and therefore the server restarts and i refresh this page still doesn't work the reason for this is that if i make it available publicly i actually have to remove the route so the folder name and just access the file name and then it does work as you can see now would be better if we also had the folder name still that is why i will use the different syntax of app use i'll pass slash uploads at the beginning to parse only requests at targeted at slash uploads and then apply this middleware and this will then ignore this part we parsed so now with that change i can reload this page and also see the image and the url's dishes still is uploads and then the file name so this is how we can now also get our files a huge advancement of course because now we can store files and get the information where they are stored and access them now feel free to build up on that and fine-tune this to your needs and with that let's move on
Info
Channel: Academind
Views: 359,056
Rating: undefined out of 5
Keywords: node, nodejs, rest, restful, rest api, tutorial, image upload, upload
Id: srPXMt1Q0nY
Channel Id: undefined
Length: 21min 34sec (1294 seconds)
Published: Thu Dec 21 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.