MERN Stack Image Upload Tutorial - GridFS Bucket and Material UI Dropzone

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] hey y'all wouldn't it be nice to learn right now how to do some image uploads with the mern stack using grid fs and the grid fs bucket system i think so this is what we're going to build this is the regular version styled with sas css this is the material ui drop zone version you can use it pretty much the same way as the regular version you can also use it as like a drag and drop kind of deal if you put the wrong kind of file type on it it shows you that you can't do that with this red and if you put the right kind of file on it it shows you that gray so there's our image working streamed from the back end so i made this because mern stack is awesome and i had an image upload system for mirn that uses grid fs but also relied on something that was deprecated and mongodb was recommending to upgrade to using grid fs buckets and that process took a bit of time to sort out and i didn't really see too many good tutorials out there so i figured this video and code might save other people some time this is an intermediate tutorial so i'm not explaining how reactor express work i'm kind of running through this expecting that you already know mernstack if that's not the case you'll really want to first learn mern stack and i highly recommend traversing media stuff and also academic web dev simplified and free code camp have some pretty solid merch tutorials we're going to use grid fs because mongodb has a 16 megabyte limit on the size of documents saved inside of it so when a user sends us a file we're going to break that file up into chunks that don't exceed that 16 megabyte limit and also a file that keeps track of all the chunks a collection of chunks and files is called a bucket multer is a middleware that we're going to use to parse the incoming files that users send us and it's going to make them available on the request object as the rec.file this is how multi-storage is set up when you're saving files to the server this would save incoming files to a folder named uploads on the server we're going to set up ours a bit differently to use our mongodb database instead of the server inside of the express wrap that we use the file parsing middleware on we can access the dot file property of rec multer also lets us check some really useful stuff like the file type and size of incoming files and we can configure multi to limit uploads to certain file types or sizes multi-grid fs storage is a package used to store our files in our mongodb database instead of on the server get the starter code from the link in the description fork and clone it or download it and unzip it it's basically a skeleton of a mirnap it's got an express server and a folder named client with a react app inside delete the assets folder because that's for the mern heroku template project in the package.json file there's a bunch of scripts one of them is the install all script we're going to run that from the root folder of our project and it's going to install a bunch of the dependencies that we need for the front end and for the back end it might need a minute or two to finish installing so this would be a great time to take some deep breaths and vibe out if you see any vulnerabilities after installing run npm audit fix you can replace the contents of the readme with info about whatever you're working on change the info on the package json to match your app's info but leave the scripts alone now we can install the stuff that we need for grid fs motor and multi-grid fs storage this is the main express server file you can delete all the comments in here but notice this one about how we need a dot env file with a mongodb connection string make sure you grab your connection string from mongodb it should look something like this except you're going to need to replace your password with whatever your password is let's move this up just because it looks better we're going to import some image routes that don't exist yet we'll make those in a minute we're going to use these routes that don't exist yet any request from the front end that gets sent to a route beginning with slash api image is going to get redirected to these routes now let's go make those first we're going to import mongoose and multi-grid fs storage and router from express and multer and we need the crypto package and also the path package that are both built into node and we're going to import env so that we can use environment variables we're going to grab the uri environment variable that we set up earlier we're going to use it to create a connection to our database and we're going to set these configuration options on it we declare gfs out here so that we can access it anywhere but we use this event listener to wait until the connection is open to set its value and we're going to set it equal to a grid fs bucket passing it the con.db and a bucket name of images now gfs is going to be like a mongoose model with methods on it that we can use to interact with the collection of documents inside our database and in this case it's a collection of files and chunks representing our images we're also going to change the name of this file to match the way that we're importing it in our index.js main server file because i accidentally threw an s at the end of it that we don't need there hopefully you caught that if you didn't go change that we set up our storage engine using multi-grid fs storage to store files in our database and we're going to pass the uri as the url and some configuration options we're going to pass a function named file which gives each file uploaded a unique file name to avoid naming collisions we get some random hex bytes from crypto turn them into a string and throw the original file extension on the end of it we also set the bucket name and resolve this file info object now to set up multer we pass an object with the storage we just created and configure the limits for the files accepted we're going to cap it at 20 megabytes for now and we're going to add a filter that runs incoming files through a function we're going to define below this function is straight from a traversing media tutorial we define a regular expression containing all of the file types we want to accept and use it to check the extension name and mime type of the incoming file if both match our regular expression we return the callback with null passed for the error letting the file pass through our filter otherwise the return on line 56 doesn't happen and node continues on to line 57 where we pass in an error message as the first argument to the callback function here we finally use the multur storage we defined above to create a middleware wrapping it in this other function so that we can handle errors and send back different responses based on what went wrong or continue on to the next part of the express route if everything is fine now we can make a post route to handle incoming files using our upload middleware if we want to have specific limits for this route we can check the rec.file and if it doesn't match what we want we can delete the file and send back an error message we'll define that deleting function in a minute if everything is fine for right now we'll just send back the id of the file so that the front end can use it to request the image here's our delete image function it just checks to make sure it was called with an argument then makes a mongoose object id from that argument and calls gfs.delete passing in the object id along with a function to run if an error occurs lastly we need to get route for the front end to access images with check to make sure rec prem's id exists before trying to make a object id with it though you probably want to do something like check that the id is valid as an object id before trying to make an object id with it but i'm trying to keep this brief and focus on the image stuff anyway we call gfs.find passing in an object with this underscore id and call the dot twoarray method on the return value and check if the files requested by the user exist in our collection if they don't we send an error message if they do we call gfs.open download stream passing in the object id which gives us our data stream we call the dot pipe method on our data stream passing in the response object so the data will be streamed to the user that sent the request and don't forget to export our router because we're importing it into our main server file now we can start on the front end go to the client folder in the public folder index.html let's change the title of the document now let's install some packages cd into the client folder and we need material ui core material ui icons and material ui drop zone and also axios let's start importing the components that we're going to make let's put a title in the jsx we return and also the components we're importing and let's import a style sheet we haven't made yet and let cd back into client and install sas because we're going to use a css instead of just regular css you could use just regular css but i don't know i always end up using scss so i'm going to start with it now let's make the components we wrote imports for and we'll just put some dummy text in them for right now and let's make that style sheet we wrote an import for just copy paste these from the resources link in the description it's mostly just setting up some defaults like border box and setting the font size so that one rem is 16 pixels run the command npm run dev from the root folder of the project to start up the back end on port 8000 and the front end on port 3000 okay so there's our dummy text now let's get to work on the regular input import axios a styling sheet we haven't created yet and an image that doesn't exist yet that's going to be our loading gif now we set up some use state hooks file is going to hold the file that's pretty straightforward make sure to import use state and input contains file can be used to conditionally style our input currently uploading we use to conditionally render the loading gif image id is the id sent back from the server after the upload completes and we use this in an image tags src attribute to display the uploaded file and progress will be the percentage the upload has completed all right we're going to write some functions now handle file will be our on change event listener function it's going to set our file state value with the first item of the event.target.files array and set input contains file to true so we can change the style of our input now we're going to add that stylesheet so again i'm just copy pasting this over you should grab it from the resources files linked in the description main thing here is we're just hiding the actual input itself and we're styling the label there's our loading dots and some styles for the image when we display the image that we get back from the server so add those save that let's make a folder for our images grab the loading gif from the resource link in the description and drop it into the images folder we've got the import written for it already so we can start our jsx now let's add a class name of regular to this div here and we'll put another div inside with the class name of input container in here we're going to conditionally render our loading dots if we aren't currently uploading then we render the input and its label the class name of file input will hide the input itself for on change event listener we pass our handle file function and we're going to put a type name and id of file let's add a label with a class name of input label [Music] and a conditional class that will only have one file contains a value html4 needs to be filed because this links it to the input above put a click event listener on this label when it's clicked we check if there is currently a file you could just use file instead of input contains file i just put it for clarity but it might be clutter so whatever you want to do if there's a file let's set currently uploading to true and call a function to send our file to the server now to write that function make a new form data object append our file to it and make a post request to the server with axios using that form data as the body inside the options object we listen for on upload progress events and when one occurs we set our progress state value with the percentage completed and let's log it to the console as well in the dot then we handle the response from the server we'll destructure the data property from that response set image id to data set the file state value to null set input contains file to false since we uploaded the file and are done with it set currently uploading to false since the upload process is over and let's throw catch on the end for error handling we can decide what to do based on the error code the error handling is really up to you this is just to serve as an example looks like we've got an error message because i missed type 2 of our variables all right now it runs but the label is missing so let's go put some text in there if file exists this label should say submit if there's no file it should just say regular version looks like i mistyped the class name here input label make sure all your class names match the ones in regular.scss now let's try it again we can see our file was successfully uploaded to the backend and saved to the database because it's logged in the terminal from the upload route but we need an image tag on the front end to request and display the file let's make a div with the class name image section here if image id exists we display an image tag with a class name image and an src set to api slash image slash whatever the image id value is never forget your alt tags and let's put a link with the class name link and an href with the same path as the src on the image set target to underscore blank so it opens in a new window and if no image id exists let's put a paragraph tag that says so now let's try again there's our image and the link opens the image in a new window one last thing here let's move these two lines outside of that also they always happen inside of the catch now for the material ui version import use state import make styles and button from material ui import drop zone dialog from material ui drop zone and import axios now we make the use styles hook with make styles again copy paste from the resources files linked in the description because we're not really going over styles here now use that hook inside of the mui version component to make our classes and let's set up some state show conditionally renders the drop zone input modal image file stores the file progress is the percentage uploaded image id will be the image file id sent back from our upload route after we upload a file currently uploading is also the same as before a boolean for conditional rendering now we need our functions handle file will destructure the first item from the array we get from the input notice the brackets around the argument if the image exists we're going to set our image file state value with it handle delete will reset that state to null handle submit you can go two ways with you could use the file that we've been saving in our own state or you could just get the file from the event when it fires since the mui component will include it there when a user submits because it has its own internal state so you really only actually need our image file state value if you want to keep track of the image file yourself outside of that mui component our handle submit is essentially going to be the same as the regular version we're going to make a form data object append the file use the form data object as the body on a post request and keep track of the upload progress i'll show you what this looks like on a slow connection in a minute our dot then handles successful responses so we set our image id state with the data from that response and reset other state values our dot catch handles unsuccessful post request responses so we handle the error based on its status code and data and reset some state values but don't clear the image file state in case we want to still be able to work with it let's put this one in the regular version too to make it more clear now we need some jsx let's add the class name mui version to this element and here we put the drop zone dialog this is the modal open is our boolean state value for conditional rendering onchange is our handle file function onclose is going to set the show state to false ondelete is our handle delete function accepted files is an array of file types that we allow max file size is exactly what it sounds like in bytes files limit we set to one for now for single file uploads set show file names in preview to false because long file names overflow same thing for show file names drop zone text is whatever you want it to say get file added message is also whatever you want it to say but it has to be a function same for get file removed message it needs to be a function that returns a string for on alert let's just console log the output get file exceeds message makes an error message when the file is too large get drop reject message returns an error message to be displayed anytime a drop input is rejected onsave is going to be our handle submit function now we need a button to show this modal sick that works now that i think about it for the get drop reject message you'd probably want to check the file that the user attempted to input and return different messages based on what went wrong since this method will be used anytime any drop input fails the file can be accessed as the first argument for this function here you can check the properties on it like the dot size or dot type now we need the image section again we conditional render based on image id if it exists we display the image and link if it doesn't we say so in a paragraph tag let's test it out now our mui version is also working here's the drop input and the link is working as well so this is what it looks like if you use our image uploading system here on a slow 3g connection that's being logged to the console as our image is uploaded those are the values that our progress state value is being set to so you can easily imagine how you might use that progress state value to conditionally style a loading bar setting the width of a child element to a string containing the progress value and a percentage symbol and having that child element inside of a parent element which assumes the full width you want the loading bar to reach you could also conditionally style rgb values for the background color to change an element's color as the upload progresses however on a faster connection it will happen pretty much instantly if you're just uploading images but if you were to upload larger video files or something then you might start to see some values between 0 and 100 here so integrating this into a project with authentication and user accounts might look something like this you would probably have some off middleware if you're using json web tokens the main thing here is just that this middleware validates the token header of a request and if it's valid it decodes the user id from it and makes it available on the request object before passing it on to the next middleware you can also use cookies or whatever but just make sure you are using authentication for user profile updates and we're talking mern of course so you would have some mongoose models your user model might look something like this main point here is just that there's a property named profile pic and it's of type string i'll make a copy of the image routes and show what these routes would look like in that kind of app we would import our off middleware and our user model and a route for changing the profile pictures might look like this using a different path and the auth middleware then the user id can be accessed from the token user added by the middleware instead of logging the file and sending back the id after uploading like we have done for the demo we would first check if the user exists if there's no user send an error message if there is a user our logic continues past that return and checks if the user has a profile picture already in a simple app maybe we want to limit them to one photo so we can cast the picture id to a mongoose object id inside of a try catch and then delete the current picture assuming everything goes right let's update our user setting their profile pic property to the new files id and add our config object in the dot then we send back a profile pic in the dot catch we send back an error make sure to set new to true in the configuration options otherwise we don't get the updated version of our document inside of the dot then a more complex social media application might have a viewable record of past profile pictures with comments and likes for that the mango schema might look something like this notice that we have an owner property referencing a user and also a file id property instead of updating the user with the image file id directly we would make a new profile picture document with the user's id as the owner reference and the image file id as the file id back in our image routes we would import the profile picture model in our route we would create a new profile picture document passing in the user id as the owner and the image file id as the file id then we update our user this time setting profile pic to the underscore id of the newly created profile picture document and also pushing the old profile pictures id onto an array of ids our user model will need that property added to it so let's add an array property to it by that name in order to keep track of those past profile pictures so that's my mirn image upload system with grid fs buckets feel free to do anything you want with it some stuff you could try is uh adding to the styling you can make decisions about how to conditional render or handle the files you can reinvent the ui maybe the file submission or modal opening should be triggered by a mobile swipe event or something you can make a version that allows users to upload multiple files at a time both multer and material ui drop zone have great options for this you try integrating it into a project with authentication and users and let the users upload profile pictures cover photos image posts or send gif messages or video content or pdf articles or whatever and if you do use this in any of your projects i'd love to see them so please drop links in the comments that's all for now but i'll be back with more soon stay at it don't these emergency vehicles know that i'm recording educational content right now ridiculous
Info
Channel: SingleWingAcademy
Views: 1,775
Rating: undefined out of 5
Keywords: mern stack, mern, rectjs, mongob, imaeg upload, mernstack, mern steck, mernsteck, gridfsbucket, gridfs-bucket, gridsf bucket, react imag upload
Id: OvbRLY1QRIk
Channel Id: undefined
Length: 37min 46sec (2266 seconds)
Published: Tue May 11 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.