dotnetdevcsharp
dotnetdevcsharp

Reputation: 3980

Trying to upload a file using asp.net core

I am loading product images in from the partial shared view, I am trying to use Ifrom file which was suggested in the previous question.

So in my product controller, I have the following method

Edit

I only want to save the path of the file in the database and then save the file on the disk what is the correct way of achieving this.

public ActionResult FileUpload(ProductImages model)
{
   if (model.Image != null)
   {
      var uploads = Path.Combine(hostingEnvironment.WebRootPath, "uploads");
      var filePath = Path.Combine(uploads, GetUniqueName(model.Image.FileName));
            model.Image.CopyTo(new FileStream(filePath, FileMode.Create));
    }
        // to do  : Return something
        return RedirectToAction("Index", "Home");

}

My Product Images class

public class ProductImages
{
    [Key]
    public int ProductImageId { get; set; }
    public int ProductID { get; set; }
    public string ProductImageTitle { get; set; }
    public string ProductImageUploadUrl { get; set; }
    public string ProductImageRealPath { get; set; }
    public string ServerIpAddress { get; set; }
    public string ProductImageAltTag { get; set; }
    public int DisplayOrder { get; set; }
    public IFormFile Image { set; get; }
}

My Shared layout which I am Loading via following.

<div class="tab-pane" id="images">
        Product Images
    @await Html.PartialAsync("_ProductPicture", Model)
</div>

This is the code for my shared view I think I am doing something wrong with the way I have declared the IFormFile

@model solitude.models.ProductImages

   <div class="form-group">

    <form asp-action="FileUpload" enctype="multipart/form-data">

        <input asp-for="ImageCaption" />
        <input asp-for="ImageDescription" />
        <input asp-for="MyImage" />

        <input type="submit" />


        </form>


</div>

I am receiving the following error

AggregateException: One or more errors occurred. (The property 'ProductImages.Image' is of an interface type ('IFormFile'). If it is a navigation property manually configure the relationship for this property by casting it to a mapped entity type, otherwise ignore the property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.) System.Threading.Tasks.Task.ThrowIfExceptional(bool includeTaskCanceledExceptions)

InvalidOperationException: The property 'ProductImages.Image' is of an interface type ('IFormFile'). If it is a navigation property manually configure the relationship for this property by casting it to a mapped entity type, otherwise ignore the property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.

Edit 2

[HttpPost]
public ActionResult FileUpload(ProductImageVm model)
{
    if (model.Image != null)
    {
        var uploads = Path.Combine(hostingEnvironment.WebRootPath, "uploads");
        var fileId = GetUniqueName(model.Image.FileName);
        var filePath = Path.Combine(uploads, fileId);
        model.Image.CopyTo(new FileStream(filePath, FileMode.Create));

    // to do  : Save the record in ProductImage table
    var pi = new ProductImages { ProductID = model.ProductId };
    pi.ProductImageTitle = model.Title;
    pi.ProductImageRealPath = fileId; //Storing the fileId
    _context.ProductImages.Add(pi);
    _context.SaveChanges();

    }
    return RedirectToAction("Index", "Home");

}

Now one thing I am loading the form within another form could this be the problem

Upvotes: 0

Views: 4410

Answers (1)

Shyju
Shyju

Reputation: 218702

You should not be using IFormFile type property in your entity class. As per the comment, you want to store the image in the disk and store the path to the image in the table. So just remove the public IFormFile Image { set; get; } property from your entity class definition.

You can use IFormFile in your view model which is used to transfer data between your view and action method. So create a view model with properties needed from the view

public class ProductImageVm
{
   public int ProductId { set;get;}
   public string Title { set;get;}
   public IFormFile Image { set; get; }
   //Add other properties AS NEEDED by your view
}

Now to upload the image for product 56 ,create an object of this view model, set the ProductId to 56 and send to the view in your GET action. In the view, we will keep the productId in a hidden form field which can be used later in the HttpPost action when we need to save a new ProductImages entity record.

public IActionResult UploadImage(int productId)
{
  var vm = new ProductImageVm { ProductId=productId};
  return View(vm);
}

and the view,which is strongly typed to our view model

@model ProductImageVm    
<form asp-action="FileUpload" asp-controller="Products" method="post"
                                                        enctype="multipart/form-data">    
        <input asp-for="Title" />
        <input asp-for="ProductId" type="hidden" />
        <input asp-for="Image" />    
        <input type="submit" />       
</form>

Now in your HttpPost action method, you use the same view model as the parameter, read the Image property and save it to disk and store the path in your table. You do not need to store the full path. You can store the relative path. In the below sample, i am storing the unique file Id(the file name) to the ProductImageRealPath property value when saving a ProductImages entity record.

[HttpPost]
public ActionResult FileUpload(ProductImageVm model)
{
   if (model.Image != null)
   {
      var uploads = Path.Combine(hostingEnvironment.WebRootPath, "uploads");
      var fileId = GetUniqueName(model.Image.FileName);
      var filePath = Path.Combine(uploads,fileId);
      model.Image.CopyTo(new FileStream(filePath, FileMode.Create));
    }
    // to do  : Save the record in ProductImage table
    var pi=new ProductImages { ProductId = model.ProductId};
    pi.ProductImageTitle = model.Title;
    pi.ProductImageRealPath = fileId; //Storing the fileId
    db.ProductImages.Add(pi);
    db.SaveChanges();
    return RedirectToAction("Index", "Home");

}

Upvotes: 3

Related Questions