File Uploading in Rails with Carrierwave

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
so file uploading is the next feature we want to add to our application and we're going to talk about how to do this in pure rails as well as using the carrier way of gem to simplify things considerably so what do we want to upload files for well our books like mastery could use a bit of sprucing up they could use an image for the book cover and it would be nice if we could click on edit and see a file field and be able to upload an image there so that's what we're gonna do this is pretty straightforward however when we do this we're gonna need to do a fair amount of work because we have to understand how rails works internally a little bit so that we can place the files in the proper directory so that they're available for the browser so if we jump into our terminal and we take a look at the public folder this is pretty special so the public folder is static files that are served up by your web server and they hit before rails does so if you type in 404 dot HTML it will bring up the 404 page just like you see here but this is not being served up through rails it sees 404 dot HTML inside this folder right here and it immediately serves it up so there's no processing going on there's no database lookups or hitting your rails routes or any of those things it sees the file name and it serves it up if this didn't exist then it would go through rails and try to look for a route and go through the regular process but it doesn't because this URL matches exactly that so we're going to take advantage of this knowledge and we're going to put in here a folder for the book in the book covers so we're gonna have to design how that works so that we can properly implement this and handle it in our terminal we can go into the same public directory and list out the files and if we think about it is the perfect place to put our uploaded images we can store them here and they'll be served up without ever processing through rails which will be very quick compared to going through rails since their static files so in here we want to separate everything out very cleanly and because things can change in the future and we want to be ready for that so for now we're just uploading images for books but what if we want to add avatars for users later on well that means that we should separate those out into folders at the highest level so if we make a directory called books here we can store all of the images and a file uploads for bucks if we make images and avatars for users then we can have a user's one and separate those two out cleanly so inside our books let's talk about what else could change what if our books have multiple images what if we have an image for the book cover for the back of the book what if we have an image for the author all of these mean that we can have more folders inside of here for every single book so at the next level we want to add a folder for a dynamic folder name that is the book database ID or the database slug so we're going to use the database ID because the slugs can change and we're just gonna stick with the IDS to keep it simple so book number two I happen to know is mastery and we're gonna hard code an image in here and test it all out so if we create book number two here and we dive into that then we can talk about what we just mentioned where what if the book has a cover a back cover or an author image so in here we want to separate those out we don't have those yet but we want to think about it in case we ever do so here we're gonna put the cover folder and then inside the cover folder you're going to be able to upload the file directly to this folder so I'm going to simulate that by just copying an image in here and saving it so we could rename the images but it doesn't really benefit us that much if we keep the original file name we can save this mastery cover JPEG into the database and then our book knows that hey it does have a cover so if that field is empty in our database record then it knows that there is no cover and we can use regular rails active record code to skip the image and if it does have one it can look and point the image tag to this folder so this is how we want to structure the folders and the image uploading at the very lowest level on your file system so if we dive into our code we can in the show view we can do something like this which is still somewhat hard-coded so we can say there's an image tag for slash books slash book ID and the cover and the file name for that book cover and if we open this up now in our browser go to mastery the image loads so this knows to automatically look for the image where we stored it and if you paste the image URL into your browser you can see that it's book slash to slash cover mastery cover JPEG which is exactly the same folder and file name as we just create an inner terminal now that we figured out where we want to save our images on disk we can go to the edit form and the new form and begin adding the file upload field as we had planned on in the very beginning so we need to add the field we need to have it saved the file and we also need to save the file name into the database record so let's start with the form if we open our form we can add a new forum group for an attribute that I'm going to call cover so we can add a file field here for cover and save our form group and now if we refresh the page we have a cover attribute that we can upload so if we upload the mastery cover again an update book nothing changed nothing crashed but it didn't actually do anything and the reason why is because if we go to our rails logs we can come back to the patch method that we just saw and we can see unpermitted parameters cover so the cover file is being uploaded and as you can see here it is an action dispatch uploaded file instance it has a temp file assigned to it and we can see the file name and the content type and everything about it and that means that our file is being uploaded correctly however we're just ignoring it when we receive it so we need to go into our books controller to actually allow it to happen so if you jump down to the bottom you can add cover into the book params method as a permitted attribute so this will now allow the cover image to be submitted and it will try to assign it to the book however on the book we don't actually have anything but friendly ID on there we've never added a method for this cover and we don't even have an attribute for the cover file name that we need to say it so let's start there let's go into our terminal and generate a migration called add cover file name two books and we'll add the cover file name attribute as a string and run our migrations we finally have everything we need to actually upload and process this image and that's what we're gonna do now so if we add an adder access err for cover this allows the controller to assign the image we uploaded to the cover attribute on this book and then we can go doing the actual saving into the file system where we want so if we add an after saved call back and we'll call it save cover image this allows us to take that temporary file that we uploaded and really save it into the public directory like we talked about so we only want to do this if there's a cover image so if this is nil then we won't try to save the image again so then we can add save cover image method if I spell it right we can then take and set up a couple things for the this method so first we want the file name that we want to save to and the file name we want to keep the exact same is the cover and we'll just assign it the original file name so the next thing that we want is we want to save the folder to a variable and this is going to be on our rails and server file system and this is going to have the database ID in the cover folder in it and then next we want to actually create this folder so we can use a library called file utils that has a Maitre underscore key method on it that allows you to create this entire folder structure if it didn't exist it could do it all in one swoop so it's nice and convenient for us rather than having to check if public exists if book exists if ID exists if cover exists it can do it all at once for us so once that folder is created we can actually go and copy the cover image into this new folder so I'm going to do it by creating an you file and we're going to join the folder and the file name together so this file dot join is going to say public books to slash cover and then slash mastery cover jpg for example and then we want to make it a writable file and we're going to write to it with the contents of the cover image and then we can close writing the file and last but not least we need to get rid of the cover image now that we're done with it so that next time this wouldn't execute again so it'll only happen once and then we can update this book with the cover file name in the file name that we had set up here so what this does very simply is it creates the folder it writes the file to it and then it updates the book record with the file name so we can add a new file different file name and it will always know which one to point to and this line is very important because if you don't have it it will go into an infinite loop and continue trying to save this cover image over and over and over again now that we have the file uploading working we can go into our show action and we can remove the hard-coded file name that we had before and replace it with book cover file name and at the end here we can add if at book that cover file name question mark so this will only display the image if the book has had an image uploaded for it so now we can go into our public folder again and let's remove the images that we have and the folder that we created before and then we can go to our application we can go into mastery there is no image being displayed and if we edit and we upload masteries cover image now it displays and if we go back and we go to how to win friends and influence people and do the same thing we can see that it also displays properly if we go to one of the books that I haven't played with you can see that of course there is no image here while this code isn't terribly complex it starts to get pretty nasty if we start adding in image editing like cropping and resizing and even doing work like uploading these images to Amazon s3 or Rackspace cloud files makes this a whole heck of a lot more complex not to mention if you want to add an author image here you pretty much have to duplicate everything we just wrote so what we're going to talk about next is how you can use carrier wave to replace this but now that you have a good understanding of why this is important and how carrier wave works at the very most basic level you'll be able to actually use carrier wave pretty extensively and it won't feel like just random magic that someone put together for you so now that we've designed our own mechanism for uploading and storing files on our server let's take a look at how carrier wave does it now the one thing that I want to point out here is that carrier wave has a concept of an uploader and an uploader is a ruby class that is defined and the haratz from carrier waves internal helpers so it has a class that you basically you mount on your active record model that says okay any interaction with this cover attribute will be through carrier wave so it's going to help you store the file whether that's on your local server or on Amazon s3 or Rackspace cloud and it's also going to handle all of the image processing that happens and you can also do things such as configuring the storage directory where your files are saved so this is what the uploader is designed for it's to encapsulate all of the logic that happens inside of your application when an image is uploaded and the reason why they do this is be because like we talked about earlier if you were to have an image for the book cover as well as an image for the author you might want to process those separately so one of them might need to be a certain size and the other one might need to be a different size now you can create two uploaders and separate all of that code out very cleanly in between the two so to transfer over our custom file uploading system to carrier wave we're gonna first install the carrier way of gem and that goes in our gem file at the bottom and we can run bundle install and restart our rails server and then we can run the rails generate uploader command to generate a carrier wave uploader I'm going to call this one cover so that it generates the cover uploader and this will be what we use to handle cropping or whatever else we want to do with the cover images so now that this is generated let's take a look at what it does in here we can see at the top there's a couple comments for plugins to carrier wave that you can install that allow you to use image magic to do image cropping and scaling so if you'd like you can follow the carrier wave readme and learn about how to install image magic and enable these features the next one is a storage file which basically tells it saved to the storage directory here that we've chosen so this is very similar to how we laid out the folder structure for our book covers what they do is they have the class name of the book first then they have cover which is what the uploader is mounted as and then the model idea so that is how the file storage saves to a certain location and then at the bottom you can override file names and you can also enable a whitelist of extensions so that only JPEGs or gifs or PNG is for example are allowed and the option underneath storage file is called storage fog and fog is the rubygem that allows you to interact with remote file systems basically so if you're going to use Amazon s3 or Rackspace cloud files or you want to do something different you can use fog to interact with those remote systems and then when carrier wave receives a file that's uploaded it will go and save them remotely so this is how carrier wave defines all of its custom for an upload and you just simply configure it here there are only two more things we need to do to finish installing carrier wave and that is to rename our column on our books table and change cover file name to cover and then we need to tell the books model that the cover database column is where we want to store the uploaded files so the first thing we need to do is to run a migration to rename cover file name and we will just dive into our editor and add in the rename method here so if we go to our migration we can add rename column on the books table and we want to rename cover file name to cover so we can save that and run rake to be migrated to actually run that and the last thing that we need to do is go into our book model and we delete all the code that we wrote before and we can simply replace that with mount uploader on the cover attribute and we will mount the cover uploader to handle it so anytime that a file is assigned to the cover attribute on a book carrier wave will step in and handle it and do everything that you've defined in the cover uploader so with that we can take a look at our rails application it's uninitialized constant because we need to restart our rails application and if we restart it now everything is set up properly after we've installed carrier wave and now we can come into our book and we'll see that the image tag that we're using before no longer works because we don't have the cover file name attribute so with carrier wave the way to access the images it's very simple and much cleaner than what we wrote before so here's what we were wrote before and here's what you can do with carrier wave so you can say image tag for at book cover dot URL and that's as simple as it is so they take the cover attribute and add some methods on to it to retrieve the URL for it and we had to build the URL ourselves now we could have spent a whole bunch of time you know moving this in to make something fat a bowl with carrier wave but there isn't really much point in doing that when you can just use carrier wave and we can let's just comment this out and take a look and make sure that the new image tag is working but because we've uploaded the images to a different a slightly different folder name it's not available so we're going to add the same thing here and say if app book cover question mark and make it so that the image does not display if carrier wave doesn't see a cover and this is a little bit better just because it's very clear if there's a book cover so our code is really readable this way with carrier wave and it makes a big difference as your application gets bigger so we should be able to now go into edit and we'll reload the mastery cover JPEG and there we go if we want to compare this to before we can open the image in a new tab and take a look at the URL which is just about the same it's now at an uploads book cover and then the database ID so ours was the database ID first it doesn't really matter either way the way that I designed it where you have the database ID first means that all of the images for a single record are in the same folder which can be pretty convenient if you're gonna do some manual work of like messing with images on the server so that is carrier wave and I hope you learned quite a bit about file uploading there's a whole ton to it and I highly recommend checking out fog and playing with Amazon s3 because I think it's free for a year and it's worth checking out
Info
Channel: GoRails
Views: 26,296
Rating: undefined out of 5
Keywords: Ruby, on, Rails, Ruby On Rails (Software)
Id: Q8wF9RrJhrY
Channel Id: undefined
Length: 23min 22sec (1402 seconds)
Published: Mon May 18 2015
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.