Persisting a GIF From the Controller7:42 with Chris Ramacciotti
As the final step for uploading a new GIF, in this video we add the necessary controller code to call upon the GifService for saving the uploaded GIF.
GIFs for testing
If you attempt to upload a file larger than 1MB, you'll run into an error. This is because by default, the
MultipartFile's max size is set to 1MB. Check out this Spring guide to tune the max size. Or, download these to stay under 1MB:
Git Command to Sync Your Code to the Start of this Video
git checkout -f s5v4
Using Github With This Course
You can complete this course entirely using code created by you on your local machine. However, if you choose to use the code I've made available to you, you have two options:
- Use the project files linked at the bottom of this page, or
- Use the Github repository I've made available (recommended)
If you choose the recommended option of using the Github repository, it can be found at
To utilize Github with this course, you can download the Github desktop client for your system or use the command line interface provided with Git.
Clone this repository to your machine using the Github desktop client, or using the following command:
git clone email@example.com:treehouse/giflib-hibernate.git
To update your local repository to match a video's starting point, you can use the
git checkout command in combination with the stage and video number. For example, to update your local repository to match the starting point of Stage 5, Video 4, you'd use the following:
git checkout -f s5v4
Notice the use of the -f option. This forces Git to override all local changes, so be aware: this will cause any changes you made to be lost.
Using a Custom Validator on the Uploaded GIF
Adding validation to the GIF can be a bit tricky, with one reason being that the
MultipartFile is not part of the entity, but should definitely be validated. Currently our
Gif entity uses a
byte field for the GIF's binary image data. We're capturing a
MultipartFile object in the controller method with a
@RequestParam parameter. A nice alternative would be to include the
MultipartFile object as a field in the entity, but we don't want this object to be persisted to the database, so we can mark it as
@Transient (which means it'll exist in our application, but won't be saved to or read from the database).
The advantage of including it as a field is that we can then validate the entire
Gif entity (with the file), and not have to worry about that logic in the controller methods for uploading a new GIF and updating an existing GIF. In order to do this, though, we'll need to write a custom validator.
Check out my Gist on Github to see everything you'll need to make this happen.
It's time to wrap up the controller's method for uploading, 0:00 before testing this whole upload functionality in the browser. 0:03 Now we'll first need to autowire the service, so I'll do that up top. 0:06 Autowired and we'll add a private GifService and I'll call it gifService. 0:12 Great. 0:18 Then we can return to the add gif method to finish our uploading work. 0:20 I'm going to use the structure feature that i showed you earlier, 0:24 addGif, and there is that method right there, cool. 0:27 To save the GIF entity, we'll invoke the services save method, 0:32 passing it the GIF entity, as well as the multipart file object. 0:35 As for the GIF entity, we can add it as a parameter here and 0:39 Spring will automatically reassemble the form submission data into a GIF entity, so 0:44 I'll add that right here. 0:49 And now let's call the services save method. 0:52 We'll do that right here. 0:55 So we'll call gifService.save, 0:57 we'll pass the GIF object that this controller method received, and 1:00 we'll pass the multipart file object that this controller method received as well. 1:04 After we save, we should include a flash message indicating that the save completed 1:11 successfully. 1:15 So, let me add a comment for that. 1:16 We'll add a flash message for success. 1:17 So, as I try to do that, 1:22 I realize I don't have access to a redirect attributes object. 1:24 That's because it's missing from my parameter list here, so let's add that 1:27 redirectAttributes and I'll name it the same thing and there we have access to it. 1:31 RedirectAttributes.addFlashAttribute, remember we call that attribute flash 1:37 and the object will add is a new FlashMessage object. 1:45 Here, we'll Type a status of GIF successfully 1:49 uploaded, or whatever message you like and don't forget the status. 1:55 So that's going to be FlashMessage.Status.SUCCESS, 2:00 cool, semi-colon. 2:05 As for the re-direct, let's send the browser to the newly uploaded GIF's detail 2:07 page, which is /gifs/ whatever the ID happens to be. 2:13 Here's how we'll do that, in place of null, I'll use the String.format function 2:19 and I want it to redirect to /gifs/ and 2:24 then what ever that ID happens to be. 2:30 And what is that value that we want placed in place of this placeholder right here? 2:34 Well, gif.getId. 2:40 We can get the entity's ID by calling it's getId method, and this will return a value 2:45 now because it will have been persisted to the database following this save up here. 2:50 The hibernate session save method takes care of updating the ID field for 2:58 us using it's setter, so we don't have to do that explicitly. 3:02 Now there are two more items we need to take care of in the controller. 3:06 The first is fully implementing the method for a give detail view. 3:10 Since that's exactly where we're redirecting to, 3:13 right here we're redirecting to the gif detail view /give/ whatever that 3:17 ID happens to be 1, 2, 7, 13, 1834, whatever it happens to be. 3:22 So we'll need to code that controller method that handles the detail view. 3:28 And the second thing we need to do is to make sure we're able to serve the GIF's 3:33 image data so that when it's referenced using an img tag in HTML, 3:37 our app can properly serve the image so the GIFs that the users so 3:42 enthusiastically uploaded can be displayed. 3:48 Let's work on the GIF detail view first, and 3:52 that is rendered by the GIF details method up top here. 3:55 Now that we have a findById method in our gifService, 4:00 we can invoke it here with our autowired service. 4:04 So we'll say gifService.findById, and 4:06 we'll use that gifId that we received in here, gifId. 4:11 Now for the second piece that is the actual image data, 4:16 which is right below this one in this gifImage method right here. 4:18 We need to fetch the GIF in the same way we just did in the method above. 4:24 So let's first grab the GIF entity using the service gifService.findById, 4:30 let's use that GIF id that's given as a parameter value there, and gifId. 4:39 Cool. 4:44 And then, let's return the byte array from the entity using the getBytes method, 4:46 .getBytes. 4:50 Cool. 4:52 And that should do it. 4:54 Notice that I have applied the response body annotation here. 4:55 What this tells Thymeleaf is that this return value here 4:59 constitutes the exact response body that should be sent with the HTTP response. 5:04 That is, 5:11 we don't want the Thymeleaf template engine to render any sort of view here. 5:12 We want Spring to use exactly this return value as the response body, 5:18 instead of timely doing extra processing using a template. 5:24 This will return the byte array which will then render in the browser view. 5:28 Now, if we've been careful enough along the way, we now have uploads working. 5:35 So there's only one way to find out. 5:39 Let's test it! 5:41 So I'll make sure to save all my work here, 5:43 make sure you have your database server running. 5:46 I'm going to kill my last instance of the app and 5:48 I'm going to re-run that Bootrun task. 5:51 I've got my toes and fingers crossed for successful compilation, 5:56 there we go, and hopefully everything starts up as it should. 6:01 The application has started successfully so let's switch to Chrome and 6:05 check it out. 6:09 We've got our upload form here which is looking good. 6:10 So what I'll do here is all upload a GIF that was originally used in Spring Basics. 6:14 I'll browse it, it's in my Downloads directory. 6:18 Yes, how about this one, android-explosion? 6:21 I will open that. 6:24 I will call it Android Explosion, 6:26 looks like I've used that one before, and let's select a category. 6:27 How about Technology? 6:32 Cool, let's hit upload and see if it works. 6:35 Hey, look at that, it worked! 6:39 Now if you're seeing any sort of error in the browser make sure you head back to 6:41 your IDE, IntelliJ in my case, to see the full log of errors in your IDE's console. 6:45 That was no trivial task that you just accomplished. 6:53 Adding a file upload feature to a web application is a common feature, but 6:56 the steps needed to do so can sometimes be complex. 7:00 So, getting this to work is something you should be proud of. 7:04 I know I am. 7:07 There are various considerations that need to be made with file uploads, 7:09 namely making sure we limit the size of the files we allowed users to upload and 7:12 ensuring that malicious files are not uploaded. 7:17 And this can be accomplished by checking to see if the file is indeed a GIF. 7:20 This kind of data validation requires us to leverage the custom validation 7:26 capabilities of the Spring framework, which is outside the scope of this course. 7:29 Check the teacher's notes for more information and know that you'll have 7:35 a feature that's waiting for you to address when we're all wrapped up here. 7:38
You need to sign up for Treehouse in order to download course files.Sign up