Join Jess Chadwick for an in-depth discussion in this video Solution: Process an uploaded image file, part of ASP.NET Core: Razor Pages.
- [Instructor] Like the implementation of an HTML form, the solution to this challenge involves changes to both the client side and server side. In order to solve this challenge, you had to do three things. Add a new property to the page model to bind the image file information to, add a new form field to the markup pointing to this new property on the page model, and finally copying the posted image data from the new page model property to the recipe itself.
So let's take a look at how I implemented my solution. I'll start with step one, adding the new property to the page model just to handle the uploaded file. As I mentioned in the introduction to the challenge, the type of this new property should be IFormFile. And IFormFile lives in the Microsoft.AspNetCore.Http name space so be sure to import that. Then I'll give the new property a wonderfully creative name.
Let's call it image. Then I'll use the bind property attribute on this new property just like I did on the recipe property above it. Next, I'll switch to the markup and add a new form field that refers to this new image property. Remember though, I mentioned in the challenge that in order for the file upload to work, you'll need to set the encoding type on the form to multipart form data.
Then to add the file upload field, I'll simply create a new input tag with the type file. And I'll use the asp for tag helper to wire the field up to the new property that I just added. Then I'll use the asp for tag helper again to create a label for the field. And while I'm at it, I'll add some css classes for styling purposes.
With this markup in place, I'll switch back to the page model and implement the interesting part. Since we're working with posted form data, I'm going to implement this logic in the OnPost async method. What I want to do here is to read the new image property and save its contents to the recipe. However, keep in mind the additional requirement that I added which is that image data of the recipe should only be modified when a new image has been posted.
Also remember that the last time we saw this method in action, Razor Pages model binding was overwriting the image property with a null value because no image data was found in the form data. That means in order to implement this functionality properly, we just can't save the updated recipe property directly. Instead we'll need to retrieve the recipe from the database and update each field manually to control what gets updated and when.
So I'll start the processing in the OnPost async method by retrieving the recipe using the same exact logic that I used to load it in the OnGet method, replacing line 43 in the process since it's no longer necessary. I'll also update the save and redirect methods to point to this local instance instead of the recipe property. Then even though we can't save the model bound recipe property directly, we can still take advantage of model binding by copying the posted data from it and updating this instance that we just retrieved from the database.
First I'll copy the name property. Then the description property. The ingredients property. And finally, the directions property. Now we get to the good stuff, the image data.
At this point in processing, the image data is still intact on the recipe that we retrieved from the database. So I can now choose whether or not I want to update it. And I'll base that decision on whether a new image file has been posted with the form. To figure that out, I can check the new image property that I just added. If this property is not null, that means an image has been uploaded and we need to copy its data to the recipe.
Here's the tricky part. In order to retrieve the uploaded data from the image property, I'll first need to copy it to a temporary stream and then read the image's bytes from that stream. As I mentioned in the challenge, I can use a new instance of the system.IO.MemoryStream object and I'll wrap it in a Using statement to be sure that it's cleaned up after we're done with it. Then I can copy the uploaded image bytes from the image property to the image stream using its CopyToAsync method.
And once I've gotten the image data, I can convert it to bytes and assign them to the recipe object by calling the memory streams ToArray method. And finally, I'll also need to get the image's content type from the meta data on the uploaded image file property and copy that to the recipe as well. And that's it. With this in place, I can take the site for a spin and update the images on my recipes. For instance, I can update the pumpkin pie recipe and change its image to a new one.
When I save, I can tell that everything worked because I can see my newly uploaded image.
- Creating a new application
- Setting up pages
- Rendering dynamic content
- Reusing markup with layouts
- Increasing the maintainability of pages
- Processing data
- Validating input
- Securing an application