Uploading an Image | Creating a REST API with Node.js
- January 2, 2024
- Posted by: MainInstructor
- Category: Go JavaScript Node
Video Title: Uploading an Image | Creating a REST API with Node.js
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
-
Sale!
Wireless WIFI Repeater Extender Amplifier Booster 300Mbps
$29.99$14.99 Add to cartWireless WIFI Repeater Extender Amplifier Booster 300Mbps
Categories: Electronics, Wi-Fi Router, Wireless Wi-Fi Extender Tags: 300Mbps, 802.11N, Amplifier, Booster, Extender, mobile wi-fi booster, Remote, WIFI, Wireless, Wireless WIFI, Wireless WIFI Repeater, Wireless WIFI Repeater Extender, Wireless WIFI Repeater Extender Amplifier, Wireless WIFI Repeater Extender Amplifier Booster, Wireless WIFI Repeater Extender Amplifier Booster 300Mbps$29.99$14.99 -
Sale!
Full RGB Light Design Gaming Headset Headphones with Mic
$24.99$14.99 Add to cartFull RGB Light Design Gaming Headset Headphones with Mic
Categories: Electronics, Gaming, Gaming Headsets Tags: Design, Full, Full RGB Light Design Gaming Headset, Full RGB Light Design Gaming Headset Headphones, Full RGB Light Design Gaming Headset Headphones with Mic, Gamer, Gaming, Gaming Headset Headphones, gaming headset wireless, Headphone, Headphones, Headset, Light, Mic, Package, RGB$24.99$14.99 -
Sale!
Wireless BlueTooth Multi-Device Keyboard Mouse Combo
$39.99$19.99 Add to cartWireless BlueTooth Multi-Device Keyboard Mouse Combo
Categories: Electronics, Gaming, Gaming Keyboards, Keyboard Mouse Combos Tags: Combo, Keyboard, keyboard mouse combos, Mouse, MultiDevice, Set, WireKeyboard Mouse Combo, Wireless, Wireless BlueTooth Keyboard Mouse Combo, Wireless BlueTooth Keyboard Mouse Combos, Wireless BlueTooth Multi-Device Keyboard Mouse Combo, Wireless BlueTooth Multi-Device Keyboard Mouse Combos$39.99$19.99 -
Sale!
High Back Leather Executive Adjustable Swivel Gaming Chair with Headrest and Lumbar
$199.99$139.99 Add to cartHigh Back Leather Executive Adjustable Swivel Gaming Chair with Headrest and Lumbar
Categories: Gaming, Gaming Chairs Tags: Adjustable, Chair, computer chairs, Desk, Executive, Gaming, Girl, Headrest, High, High Back Leather Executive Adjustable Swivel Gaming Chair, High Back Leather Executive Adjustable Swivel Gaming Chair with Headrest, High Back Leather Executive Adjustable Swivel Gaming Chair with Headrest and Lumbar, High Back Leather Executive Adjustable Swivel Gaming Chairs, Leather, Lumbar, Office, Racing, Swivel$199.99$139.99 -
Sale!
Professional LED Light Wired Gaming Headphones with Noise Cancelling Microphone
$29.99$19.99 Select optionsProfessional LED Light Wired Gaming Headphones with Noise Cancelling Microphone
SKU: N/A Categories: Electronics, Gaming, Gaming Headsets Tags: Cancelling, Gaming, Gaming Headphones with Noise Cancelling Microphone, gaming headset, Headphones, Headset, LED, Light, Mic, Microphone, Noise, Professional, Professional LED Light Wired Gaming Headphones, Professional LED Light Wired Gaming Headphones with Noise Cancelling Microphone, Wired, Wired Gaming Headphones, Wired Gaming Headphones with Noise Cancelling Microphone$29.99$19.99 -
Sale!
Gaming Desk with LED Lights USB Power Outlets and Charging Ports
$349.99$249.99 Select optionsGaming Desk with LED Lights USB Power Outlets and Charging Ports
SKU: N/A Categories: Computer Desk, Gaming, Gaming Desk Tags: and Charging Ports, Charging, Desk, Desks, Gaming, gaming desk with led lights, Gaming Desks with LED Lights, Home, LED, Lights, Monitor, Office, Outlets, Port, Power, Room, Stand, USB, USB Power Outlets, White, Workstation$349.99$249.99 -
Sale!
Wired Mixed Backlit Anti-Ghosting Gaming Keyboard
$99.99$79.99 Add to cartWired Mixed Backlit Anti-Ghosting Gaming Keyboard
Categories: Electronics, Gaming, Gaming Keyboards Tags: Antighosting, Backlit, Blue, brown, Gaming, Gaming Keyboard, gaming keyboards, gaming keyboards and mouse, Keyboard, Laptop, Switch, Wired, Wired Mixed Backlit Anti-Ghosting Gaming Keyboard, Wired Mixed Backlit Anti-Ghosting Gaming Keyboards, Wired Mixed Backlit Gaming Keyboard$99.99$79.99 -
Sale!
Wireless Bluetooth 5.3 ANC Noise Cancellation Hi-Res Over the Ear Headphones Headset
$119.99$59.99 Add to cartWireless Bluetooth 5.3 ANC Noise Cancellation Hi-Res Over the Ear Headphones Headset
Categories: Electronics, Gaming, Gaming Headsets Tags: 5.3 ANC Noise Cancellation Hi-Res Over the Ear Headphones Headset, ANC, Audio, Bluetooth, Cancellation, Ear, Earphone, gaming headset, Headphones, Headset, Hi-Res Over the Ear Headphones Headset, HiRes, Noise, Wireless, Wireless Bluetooth 5.3 ANC Noise Cancellation Hi-Res Headphones, Wireless Bluetooth 5.3 ANC Noise Cancellation Hi-Res Over the Ear Headphones Headset, Wireless Bluetooth 5.3 ANC Noise Cancellation Hi-Res Over the Ear Headphones Headsets$119.99$59.99 -
Sale!
Wired Sports Gaming Headset Earbuds with Microphone
$19.99$9.99 Select optionsWired Sports Gaming Headset Earbuds with Microphone
SKU: N/A Categories: Gaming, Gaming Headsets Tags: Accessories, Earbud, Earphone, Earphones, Gaming, gaming headset with microphone, Headphones, Headset, IOS, Microphone, Sports, Wired, Wired Sports Gaming Headset Earbuds, Wired Sports Gaming Headset Earbuds with Microphone, Wired Sports Headset Earbuds$19.99$9.99 -
Sale!
150W Universal Multi USB Fast Charger 16 Port MAX Charging Station
$49.99$29.99 Add to cart150W Universal Multi USB Fast Charger 16 Port MAX Charging Station
Categories: Charging Stations, Electronics Tags: 150W, 150W Charging Station, 150W Universal Multi USB Charging Station, 150W Universal Multi USB Fast Charger 16 Port MAX Charging Station, 150W Universal Multi USB Fast Charger 16 Port MAX Charging Stations, 150W Universal Multi USB MAX Charging Station, 16 Port MAX Charging Station, 3.5A, Charger, Charging, Fast, laptop charging stations, Max, Multi, Port, Stand, Station, Universal, USB$49.99$29.99
Really Hellpful
Exquisite content even in 2023
best approach is to create an uploadmiddleware and push the code there
export will look like this module.exports = image
then in your route you will have
const images = (or whatever you want to name it = require("../middleware/imageupload))
router.post('your-route' , images.single())
i love you mannnnnnn
Anyone else getting a "req.file is undefined" error. I get the error in postman when trying to post a product with the product image url set to req.file.path
Hi i have a question when i run this rest api in locahost it work succefully but when i hosting this api and try to save a product its show this error . {
"error": {
"message": "Unexpected end of form"
}
thanks buddy you saved my life.
Why req.body before upload.single is undefined . do u have video about this big problem
me coming from chatgpt for best video on uploading videos backend saying by traversy media and he took me here lol
Great course, great teaching skills! simply wonderful
i was struggling with this. thank you so much
what if I want to upload and store more than one image?
Great vid !
I am getting ENOENT:no such file or directory, how to resolve that error??
Kindly do reply
Hey guys , I need to add in 11:37 min
const storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, 'uploads/')
},
filename: function(req, file, cb) {
cb(null, new Date().toISOString().slice(0,10) + file.originalname);
}
});
Couse have error with saving files to drive. Windows 10.
Great Tutarial again!
yore the .. maker
Can't thank you enough for uploading this video.. This is very helpful <3
Excellent Video
how do you delete files that have been stored in upload
thank you so much man ! this is awsome 😍
Thanks a lot!
This is an amazing video,
It has just solved my challenge for about 2 days now.
Welldone man
You have own level.
If we store the images in the "Uploads" folder then does that not mean that the image itself is in the app's folder and it's stored locally rather than on the DB
Thanks man
My images are not showing in vs code instead a message is being displayed that the file is either binary or has an unsupported something, can you help me with this problem.
This is the method I was looking for Thanks
Max is always precise and great.
Danke sehr 🥰🥰
Test
how i can send an error as a json format in case of to return error result of fileFilter control ?
What would you write for the headers if manually writing the front end fetch request? "Content-Type: ??????" Is there a way to do so to send the json and the image together like you did with form data?
What's the shortcut for auto-indenting functions chained in one line in vscode?
Thanks alot
!!Awesome tutorial! thank U
Thank yoou, this tutorial helped a lot :')
Wow! explained very well. Thanks a lot.
You have always been supper! Where have you come for us!? Great rescuer! Thank you
Thanks for the last part part and helping me understand how the image path works 🤩
Thanks for this!
Sir, I got an error when try to upload an with name, price and productImage properties
{
"error": {
"errors": {
"name": {
"name": "ValidatorError",
"message": "Path `name` is required.",
"properties": {
"message": "Path `name` is required.",
"type": "required",
"path": "name"
},
"kind": "required",
"path": "name"
},
"price": {
"name": "ValidatorError",
"message": "Path `price` is required.",
"properties": {
"message": "Path `price` is required.",
"type": "required",
"path": "price"
},
"kind": "required",
"path": "price"
}
},
"_message": "Product validation failed",
"name": "ValidationError",
"message": "Product validation failed: name: Path `name` is required., price: Path `price` is required."
}
}
I have an issue with my code or time configuration I believe, I have the exact same code as in the video:
const storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, './uploads/');
},
filename: function(req, file, cb) {
cb(null, new Date().toISOString() + file.originalname);
}
});
But I get … "message": "ENOENT: no such file or directory, open 'C:\Users\Facu\Desktop\RestAPI\uploads\2021-09-29T05:20:39.310ZDiablo_IV_Logo.jpg'" in Postman as an error message.
If I remove the ''new Date().toISOString()" everything works fine ! I tried other methods such as toLocaleTimeString() and such with no success (toDateString() WORKS though so I'm guessing it's the TIME part that it doesn't like … ).
Does anyone have any ideas on how to solve this ?
16:03 How can I throw a json error to client like "res.json(…)"? Thanks! 🎅
thanks man ❤️
Thanks for the amazing video. I have a question: Is it better to store the images that user's upload to our own servers this way or use Firebase or Cloudinary (Cloudbased solutions)? Will it be sustainable in terms or cost if we only need to pay for the server where we host?