在 wwwroot 中编辑图像

Edit images in wwwroot

我是mvc的程序员, 我的目标是使用我在数据库中的图像并将其编辑为另一个图像,该图像不一定存在于我的 wwwroot 中,但存在于我的计算机中。 图片:

图片说明:我这里数据库里有一张图片,想编辑一下 单击编辑按钮 编辑图片:

但是当我按下保存按钮时出现错误: 空引用异常:Object reference not set to an instance of an object. PetShop.Client.Services.FileService.File(CreateAnimalViewModel model) in FileService.cs var path = Path.Combine(wwwPath, "Images", model.Photo!.FileName); PetShop.Client.Controllers.AdminController.EditAnimal(CreateAnimalViewModel model) in AdminController.cs await _file.File(model);

必须注意,当我尝试向 wwwroot 添加新图像但在编辑中不起作用时,服务代码确实有效

我的服务:

public class FileService : IFileService
{
    private readonly IWebHostEnvironment _environment;
    public FileService(IWebHostEnvironment environment)
    {
        _environment = environment;
    }
    public async Task<string> File([FromForm] CreateAnimalViewModel model)
    {
        string wwwPath = _environment.WebRootPath;
        var path = Path.Combine(wwwPath, "Images", model.Photo!.FileName);
        if (model.Photo.Length > 0)
        {
            using var stream = new FileStream(path, FileMode.Create);
            await model.Photo.CopyToAsync(stream);
        }
          return model.Animal!.PhotoUrl = model.Photo.FileName;
    }
}
public interface IFileService
{
    Task<string> File([FromForm] CreateAnimalViewModel model);
}

我的视图模型:

public class CreateAnimalViewModel
{
    public Animal? Animal { get; set; }
    public IFormFile Photo { get; set; }
}

我的控制器:

public async Task<IActionResult> EditAnimal(int id)
    {
        var animal = await _repo.FindAnimalById(id); 
        ViewBag.Category = new SelectList(_repository.GetCategoriesTable(), "CategoryId", "Name");
        return View(new CreateAnimalViewModel() { Animal = animal});
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> EditAnimal([FromForm] CreateAnimalViewModel model)
    {
        ModelState.Clear();
        TryValidateModel(model);

        await _file.File(model);

        if (!ModelState.IsValid)
        {
            await _repo.EditAnimal(model.Animal!);
            return RedirectToAction(nameof(Manager));
        }
        return View();
    }

我的看法:

@model PetShop.Client.Models.CreateAnimalViewModel

<div >
<form asp-action="EditAnimal" method="post" >
    <div asp-validation-summary="ModelOnly"></div><input type="hidden" asp-for="Animal!.AnimalId" id="Space"/>
<dl class="row" >
    <dt class = "col-sm-2"><label asp-for="Animal!.Name" id="Space"></label></dt>
    <dd class = "col-sm-10"><input asp-for="Animal!.Name"/><span asp-validation-for="Animal!.Name" ></span></dd>
    <dt class = "col-sm-2"><label asp-for="Animal!.BirthDate" id="Space"></label></dt>
    <dd class = "col-sm-10"><input asp-for="Animal!.BirthDate"/><span asp-validation-for="Animal!.BirthDate"></span></dd>
    <dt class = "col-sm-2"><label asp-for="Animal!.Description" id="Space"></label></dt>
    <dd class = "col-sm-10"><input asp-for="Animal!.Description"/><span asp-validation-for="Animal!.Description"></span></dd>
    <dt class = "col-sm-2"><label asp-for="Animal!.CategoryId" id="Space"></label></dt>
    <dd class = "col-sm-10"><select asp-for="Animal!.CategoryId" asp-items="ViewBag.Category"></select><span asp-validation-for="Animal!.CategoryId"></span></dd>
    <dt class = "col-sm-2"><label asp-for="Photo"></label></dt>
    <dd class = "col-sm-10"><input type="file" asp-for="Photo" accept="image/*"/>
    <span asp-validation-for="Photo"></span></dd>
    <br/> <br/> <br/>
    <input type="submit" value="Save" id="ButtonDesign"/>
</dl>
</form>
    <a asp-action="Commands"><input type="submit" value="Back to Admin Page" id="BackPageButton"/></a>

看来我只展示了文件的一部分,其他的都与问题无关

编辑post 存储库:

 public async Task<int> AddAnimal(Animal animal)
    {
        _context.Add(animal!);
        return await _context.SaveChangesAsync();
    }

    public async Task<int> EditAnimal(Animal animal)
    {
        _context.Update(animal);
        return await _context.SaveChangesAsync();
    }

public DbSet<Category> GetCategories()
    {
        var category = _context.Categories;
        return category;
    }

让我们从您的错误代码开始,

[FromForm] In EditAnimal Post Method:

You are submitting [FormBody] but here you are receiving [FromForm] this is one of the reason of your null reference exception. In your case you needn't to use that instead your can directly use class.

Photo has no handlear in controller

You are sending image file from your view. But Your controler doesn't have any handeler for that. It should have IFormFile type to receive image.

所以错误的方式:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> EditAnimal([FromForm] CreateAnimalViewModel model)
    {
        
    }

Note: Based on your correct design and architecture this is not correct way.

正确方法:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> EditAnimal(CreateAnimalViewModel model, IFormFile photo)
    {
        if (photo == null || photo.Length == 0)
        {
            return Content("File not selected");
        }
        var path = Path.Combine(_environment.WebRootPath, "images", photo.FileName);
        using (FileStream stream = new FileStream(path, FileMode.Create))
        {
            await photo.CopyToAsync(stream);
            stream.Close();
        }
        
        model.Animal!.PhotoUrl = model.Photo!.FileName;
       
        
        // Find the existing data
        var objAnimal = _context.Animals.Where(aId => aId.AnimalId == model.Animal.AnimalId).FirstOrDefault();


       if(model != null)
        {
            //Update the date with new value
            objAnimal!.AnimalId = model.Animal.AnimalId;
            objAnimal.Name = model.Animal.Name;
            objAnimal.Category = model.Animal.Category;
            objAnimal.Description = model.Animal.Description;
            objAnimal.PhotoUrl = model.Animal.PhotoUrl;
            _context.SaveChanges();
        }


        return RedirectToAction("Edit", new { id = model!.Animal.AnimalId });

    }

加载时编辑方法:

public async Task<IActionResult> Edit(int id)
        {
            var animal = await _context.Animals.FindAsync(id);
            ViewBag.Category = new SelectList(_repository.GetCategoriesTable(), "CategoryId", "Name");
            return View(new CreateAnimalViewModel() { Animal = animal, DisplayPhoto = animal!.PhotoUrl });
          
        }

Note: Here you have done another mistake, there is no point of using CreateAnimalViewModel rather you can directly return Animal Model, as you decided to use CreateAnimalViewModel in that case, the way you are binding Animal domain class to CreateAnimalViewModel you would loose PhotoUrl so here I have introduced another property in View Model to avoid extra modification which I will be using to load image on view. So the updated CreateAnimalViewModel would be:

已更新 CreateAnimalViewModel:

public class CreateAnimalViewModel
    {
        public Animal? Animal { get; set; }
        public string? DisplayPhoto { get; set; }
        public IFormFile? Photo { get; set; }
    }

Note: If you can use single ViewModel for this view in that case IFormFile? Photo property is also not required, you can directly bind the image in view. But I didn't modify your existing anything.

查看加载编辑动物:

@model DotNet6MVCWebApp.Models.CreateAnimalViewModel
<div>
    <form asp-action="EditAnimal" method="post" enctype="multipart/form-data">
        <div asp-validation-summary="ModelOnly"></div><input type="hidden" asp-for="Animal!.AnimalId" id="Space" />
        <div>
            <h4><strong>Animal Details</strong> </h4>

            <table class="table table-sm table-bordered table-striped">

                <tr>
                    <th> <label asp-for="Animal!.Name"></label></th>
                    <td> <input asp-for="Animal!.Name" class="form-control" placeholder="Enter animal name" /><span asp-validation-for="Animal!.Name"></span></td>
                </tr>
                <tr>
                    <th> <label asp-for="Animal!.Description"></label></th>
                    <td> <input asp-for="Animal!.Description" class="form-control" placeholder="Enter animal description" /><span asp-validation-for="Animal!.Description"></span></td>
                </tr>
                <tr>
                    <th> <label asp-for="Animal!.Category"></label></th>
                    <td> <input asp-for="Animal!.Category" class="form-control" placeholder="Enter animal category" /><span asp-validation-for="Animal!.Category"></span></td>
                </tr>
                <tr>
                    <th> <label asp-for="Photo"></label></th>
                    <td>
                        <img src="~/images/@Model.Animal!.PhotoUrl"
                             class="rounded-square"
                             height="50" width="75"
                             style="border:1px"
                             asp-append-version="true" accept="image/*" />
                        <input type="file" name="photo" accept="image/*" />

                    </td>
                </tr>
                <tr>
                    <th>  <button type="submit" class="btn btn-primary" style="width:107px" >Update</button></th>
                    <td> </td>
                </tr>
                <tr>
                    <th>@Html.ActionLink("Back To List", "Index", new { /* id=item.PrimaryKey */ }, new { @class = "btn btn-success" })</th>
                    <td> </td>
                </tr>
            </table>
        </div>
    </form>

</div>

最终输出:

希望它能指导您相应地修复您的异常。