How to uploade images to disk in asp.net core 2 , razor pages

Viewed 3063

i'm beginner in asp.net framework, but i really encouraged to learn and apply it. https://docs.microsoft.com/en-us/aspnet/core/tutorials/razor-pages/razor-pages-start

i followed it and everything is ok and i passed to add new models, tables.

suppose we have the Movie model which is :

public class Movie
{
    public int ID { get; set; }
    public string Title { get; set; }
    public DateTime ReleaseDate { get; set; }
    public string Genre { get; set; }
    public decimal Price { get; set; }
}

and below is the code of Create process:

public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        _context.Movie.Add(Movie);
        await _context.SaveChangesAsync();

        return RedirectToPage("./Index");
    }

Below is a section from the from :

        <div class="form-group">
            <label asp-for="Movie.Price" class="control-label"></label>
            <input asp-for="Movie.Price" class="form-control" />
            <span asp-validation-for="Movie.Price" class="text-danger"></span>
        </div>

Now i want to add a new field to the form to upload the "Cover image", so the user can browse and select the cover image for the movie to upload it in the same form.

my questions are :

  • i think to store the file name in the DB, so each movie has a column "CoverImage", then i can use it to display or delete. is this good approach ?

  • what is the best place to store the files ? in "wwwroot" or another folder or doesn't matter ?

  • what is the best or latest method to upload the image ? what should i add in the three sections of codes above? i did a search and i found many approaches and methods, but i need to know from you as experts in asp.net what is the best approach.

1 Answers

To store the image path in the table, you need to first add a property to your entity class so that it will create a column in your table.

public class Movie
{
    public int ID { get; set; }
    public string Title { get; set; }
    public DateTime ReleaseDate { get; set; }
    public string Genre { get; set; }
    public decimal Price { get; set; }
    public string ImageName  { set;get;}   // This is the new property
}

Now to receive the file from the UI , you need to add a new property to your PageModel class of type IFormFile

public class CreateModel : PageModel
{
    [BindProperty]
    public Movie Movie { set; get; }

    [BindProperty]
    public IFormFile Image { set; get; }
}

Now in your form, add an input element of type file. Make sure your form tag has an enctype attribute and it's value is set to multipart/form-data

<form method="post" enctype="multipart/form-data"> 
    <div asp-validation-summary="All"></div>

    <input type="text" placeholder="Title" asp-for="Movie.Title"/>
    <input type="text" placeholder="Genre" asp-for="Movie.Genre" />
    <input type="text" placeholder="Price" asp-for="Movie.Price"/>
    <input type="file"  asp-for="Image"/>

    <button type="submit">Save</button>
</form>

Now in your OnPost method, you can read the Image property value of the page model and save it to the disk. In asp.net core, the wwwroot directory is a special directory to keep the static assets. So you may keep the images under that. I would create a sub directory called uploads inside wwwroot.

To get the path to the wwwroot directory, you can use IHostingEnvironment. So inject that to your page model class.

public class CreateModel : PageModel
{
     private readonly YourDbContext context;
     private readonly IHostingEnvironment hostingEnvironment;
     public CreateModel(YourDbContext context,IHostingEnvironment environment)
     {
        this.hostingEnvironment = environment;
        this.context=context;
     }
    //Your existing code for properties goes here
    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
            return Page();

        if (this.Image != null)
        {
            var fileName = GetUniqueName(this.Image.FileName); 
            var uploads = Path.Combine(hostingEnvironment.WebRootPath, "uploads");
            var filePath = Path.Combine(uploads,fileName);
            this.Image.CopyTo(new FileStream(filePath, FileMode.Create));
            this.Movie.ImageName = fileName; // Set the file name
        }
        context.Movie.Add(this.Movie);
        await context.SaveChangesAsync();
        return RedirectToPage("MovieList");
    }
    private string GetUniqueName(string fileName)
    {
        fileName = Path.GetFileName(fileName);
        return Path.GetFileNameWithoutExtension(fileName)
               + "_" + Guid.NewGuid().ToString().Substring(0, 4)
               + Path.GetExtension(fileName);
    }
}

This will save the file to wwwroot/uploads and store the image name in the ImageName property of the Movie record we are saving. So when you want to display the image, read the record, and use the ImageName property and create the url. (Ex : 'yourSiteName/uploads/aaa_9521.jpg, where the aaa_9521.jpg will come from your Movie records ImageName property value)

For example, in another page where your page model has a Movies property which is a collection of Movie entities, you can do this

<table>    
    @foreach (Movie m in Model.Movies)
    {
        <tr>
            <td>@m.Title</td>
            <td>@m.Price</td>
            <td>
                <img src="~/uploads/@m.ImageName"/>
            </td>
        </tr>
    }
</table>
Related