Загрузка и получение изображений ASP.NET Core MVC

  • Михаил
  • 12 мин. на прочтение
  • 254
  • 09 Dec 2022
  • 09 Dec 2022

Изображения неизбежны почти во всех приложениях. Ранее мы реализовали CRUD-операцию с обычной формой без файла для загрузки. Когда у нас есть форма с загрузчиком файлов, дополнительных процедур немного. Итак, давайте посмотрим, как загружать и получать изображения в ASP.NET Core MVC .

Обсуждаемые подтемы:

  • Разработка формы MVC для загрузки изображения
  • Отправить форму обработки изображения
  • Сохраните изображение в файл сервера.
  • Получить загруженное обратно в представление.
  • Удалить запись изображения и файл.

Создать проект ASP.NET Core MVC

В Visual Studio 2019 выберите «Файл» > «Создать» > «Проект» (Ctrl + Shift + N). Выберите шаблон веб-приложения Asp.Net Core.

После того, как вы укажете название проекта и местоположение. Откроется новое окно следующего содержания: выберите «Веб-приложение (MVC)» и снимите флажок «Конфигурация HTTPS». Вышеуказанные шаги создадут новый проект ASP.NET Core MVC.

Определите класс модели

Давайте создадим базу данных с помощью Entity Framework Core. Для этого мы должны установить соответствующие пакеты NuGet. Поэтому щелкните правой кнопкой мыши проект в обозревателе решений, выберите «Управление пакетами NuGet», на вкладке «Обзор» найдите и установите соответствующие следующие 3 пакета.

Теперь давайте определим класс модели БД ImageModelв новой папке — Models.

//File : /Models/ImageModel.cs
public class ImageModel
{
    [Key]
    public int ImageId { get; set; }

    [Column(TypeName ="nvarchar(50)")]
    public string Title { get; set; }

    [Column(TypeName = "nvarchar(100)")]
    [DisplayName("Image Name")]
    public string ImageName { get; set; }

    [NotMapped]
    [DisplayName("Upload File")]
    public IFormFile ImageFile { get; set; }
}

Помимо первичного ключа, у нас есть столбцы  Title , которые  ImageName. ImageName используются для сохранения имени изображения. IFormFile свойство  ImageFile является  NotMapped свойством, что означает, что в базе данных не будет соответствующего столбца таблицы, который просто используется для получения опубликованного файла изображения во время отправки формы, например, свойство модели представления.

Настройка базы данных

Теперь давайте определим класс DbContext ImageDbContextдля создания и управления базой данных.

//File : /Models/ImageDbContext.cs
public class ImageDbContext:DbContext
{
    public ImageDbContext(DbContextOptions<ImageDbContext> options):base(options)
    { }

    public DbSet<ImageModel> Images { get; set; }
}

Согласно приведенному выше классу DbContext, после миграции БД будет таблица  Imagesдля сохранения записей, соответствующих классу модели ImageModel. Для взаимодействия с БД нам нужен экземпляр класса DbContext ImageDbContext. Для этого мы немного воспользуемся внедрением зависимостей ASP.NET Core.

Перед этим давайте поговорим о параметре конструктора класса optionsтипа DbContextOptions. В этот параметр опций мы должны передать следующую информацию.

  • Поставщик базы данных: укажите базу данных, которую мы хотим использовать, будь то SQL Server или MySQL и т. д.
  • Местоположение базы данных: строка подключения к базе данных, которую мы хотим создать.

Сохраним строку подключения в appsettings.jsonфайле.

{

  ....

  "ConnectionStrings": {
    "DevConnection": "Server=(local)\\sqlexpress;Database=ImageDB;Trusted_Connection=True;MultipleActiveResultSets=True;"
  }
}

Теперь позвольте мне показать вам, как создать экземпляр DbContextс внедрением зависимостей. Просто обновите Startupметод класса ConfigureServices, как показано ниже.

public void ConfigureServices(IServiceCollection services)
{
    ...

    services.AddDbContext<PaymentDetailContext>(options =>
    options.UseSqlServer(Configuration.GetConnectionString("DevConnection")));
}

Здесь мы вызвали AddDbContextметод для внедрения экземпляра PaymentDetailContext. Здесь, чтобы установить SQL Server в качестве DbProvider, UseSqlServerфункция вызывается с указанной выше строкой подключения. Как работает эта инъекция зависимостей? которые мы можем обсудить после создания контроллера MVC.

Мы сделали все для базы данных, теперь давайте сделаем миграцию. Для этого выберите проект в обозревателе решений, затем перейдите в Инструменты > Диспетчер пакетов NuGet > Консоль диспетчера пакетов. затем выполните следующие команды одну за другой.

Add-Migration "InitialCreate"
Update-Database

После успешной миграции в соответствии со строкой подключения ImageDB будет создана новая база данных с новой таблицей  Images , соответствующей классу модели.

Создайте контроллер изображений MVC с действиями CRUD

Чтобы обновлять и извлекать изображения, давайте создадим контроллер MVC. щелкните правой кнопкой мыши папку «Контроллеры» «Добавить»> «Контроллер», выберите «Контроллер MVC с представлениями с использованием шаблона Entity Framework». ImageControllerбудет создан с помощью ImageModelи ImageDbContext.

С помощью Scaffolding Mechanism будет создан новый контроллер с необходимыми методами действий для всех операций CRUD.

public class ImageController : Controller
{
    private readonly ImageDbContext _context;

    public ImageController(ImageDbContext context)
    {
        _context = context;
    }

    // GET: Image
    public async Task<IActionResult> Index()
    { ... }

    // GET: Image/Create
    public IActionResult Create()
    { ... }

    // POST: Image/Create

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Create([Bind("ImageId,Title,ImageName")] ImageModel imageModel)
    { ... }

    // GET: Image/Delete/5
    public async Task<IActionResult> Delete(int? id)
    { ... }

    // POST: Image/Delete/5
    [HttpPost, ActionName("Delete")]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> DeleteConfirmed(int id)
    { ... }
}

Теперь давайте обсудим, как внедрение зависимостей ASP.NET Core помогает этому контроллеру взаимодействовать с БД. Этот ImageControllerконструктор имеет параметр ImageDbContext(который равен _context), ASP.NET Core автоматически вставит тот же экземпляр класса в параметр в качестве его значения. Нам не нужно создавать экземпляр контекста в каждом из контроллеров MVC, если у контроллера есть параметр конструктора типа ImageDbContext. Теперь остальные методы действия могут взаимодействовать с БД, используя этот внедренный DbContext.

В приведенном выше контроллере мы показали методы действий, необходимые для демонстрации загрузки изображения. есть и другие методы действия, но они не используются в этом проекте. Давайте откроем этот проект в вашем браузере, перейдите в  «Отладка»> «Начать без отладки».

Теперь, если вы перейдете по URL:  /Image/Create, вы увидите форму с элементами управления вводом, соответствующими каждому свойству модели, кроме  ImageFile. поэтому мы должны заменить  ImageName элементы управления вводом на загрузчик изображения/файла, соответствующий  ImageFile свойству. Ведь Create.cshtmlдолжно выглядеть вот так.

//File: /Views/Image/Create.cshtml
@model ImageUpload.Models.ImageModel

@{    ViewData["Title"] = "Create"; }

<h1>Create</h1>

<h4>ImageModel</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create" enctype="multipart/form-data">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Title" class="control-label"></label>
                <input asp-for="Title" class="form-control" />
                <span asp-validation-for="Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ImageFile" class="control-label"></label>
                <input asp-for="ImageFile" accept="image/*" />
                <span asp-validation-for="ImageFile" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

С помощью  accept атрибута мы ограничили загрузчик только файлами изображений. Если в нашу форму загружен файл, мы должны установить атрибут формы  enctype как  « multipart/form-data». Если вы перезагрузите страницу, вы увидите новую форму, подобную этой.

Эта форма будет передана  Create методу действия типа POST. там также мы должны внести некоторые изменения, как показано ниже.

using Microsoft.AspNetCore.Hosting;

public class ImageController : Controller
{
    private readonly IWebHostEnvironment _hostEnvironment;

    public ImageController(..., IWebHostEnvironment hostEnvironment)
    {
        ...
        this._hostEnvironment = hostEnvironment;
    }

  ...

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Create([Bind("ImageId,Title,ImageFile")] ImageModel imageModel)
    {
        if (ModelState.IsValid)
        {
            //Save image to wwwroot/image
            string wwwRootPath = _hostEnvironment.WebRootPath;
            string fileName = Path.GetFileNameWithoutExtension(imageModel.ImageFile.FileName);
            string extension = Path.GetExtension(imageModel.ImageFile.FileName);
            imageModel.ImageName=fileName = fileName + DateTime.Now.ToString("yymmssfff") + extension;
            string path = Path.Combine(wwwRootPath + "/Image/", fileName);
            using (var fileStream = new FileStream(path,FileMode.Create))
            {
                await imageModel.ImageFile.CopyToAsync(fileStream);
            }
            //Insert record
            _context.Add(imageModel);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }
        return View(imageModel);
    }

  ....

}

Как вы знаете, это суть этой статьи. В метод действия мы привязали свойства модели – ImageIdи Title( ImageFileне ImageName).

Прежде всего, мы сохраняем изображение в папку wwwroot/Image. Для этого нам нужен физический путь до папки wwwroot, для этого мы внедрили  IWebHostEnvironmentсвойство, подобное нашему DbContext. Генерируется уникальное пользовательское имя для файла изображения, чтобы избежать дублирования файлов изображений. Используя  FileStream объект, мы можем сохранить изображение в  images папку с произвольным именем.

В качестве второго шага мы вставляем детали изображения с пользовательским именем изображения в базу данных. Теперь вы можете попробовать загрузить изображение после перестроения решения.

После успешного завершения загрузки изображения вы будете перенаправлены в индексное представление. где вставленные записи будут перечислены из таблицы базы данных – Images.

Получить и удалить изображение

Если вы нажмете  гиперссылку «Удалить»,  Delete откроется представление с отображением соответствующих сведений о записи. Чтобы отобразить загруженное изображение обратно в представление, мы должны внести несколько изменений в файл  Views/Image/Delete.cshtml.

@model ImageUpload.Models.ImageModel

@{  ViewData["Title"] = "Delete";  }

<h1>Delete</h1>

<h3>Are you sure you want to delete this?</h3>
<div>
    <h4>ImageModel</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.ImageName)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ImageName)
        </dd>
        <dt class="col-sm-2">
            Uploaded Image
        </dt>
        <dd class="col-sm-10">
            <img decoding="async" src="@("~/image/"+Model.ImageName)" asp-append-version="true" width="250px" height="250px"/>
        </dd>
    </dl>

    <form asp-action="Delete">
        <input type="hidden" asp-for="ImageId" />
        <input type="submit" value="Delete" class="btn btn-danger" /> |
        <a asp-action="Index">Back to List</a>
    </form>
</div>

Основная цель представления — подтвердить операцию удаления. с  атрибутом img атрибута управления srcиспользуется относительный путь полученного изображения. Чтобы добавить базовый путь, мы можем установить для атрибута значение  asp-append-version true .

Если вы подтвердите эту операцию удаления, она дойдет до  DeleteConfirmed метода действия типа  POST. по умолчанию он запрограммирован на удаление соответствующей записи. Наряду с этим мы должны удалить соответствующий файл изображения из папки «изображение». для этого мы можем обновить метод действия.

[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
    var imageModel = await _context.Images.FindAsync(id);

    //delete image from wwwroot/image
    var imagePath = Path.Combine(_hostEnvironment.WebRootPath,"image",imageModel.ImageName);
    if (System.IO.File.Exists(imagePath))
        System.IO.File.Delete(imagePath);
    //delete the record
    _context.Images.Remove(imageModel);
    await _context.SaveChangesAsync();
    return RedirectToAction(nameof(Index));
}

Это все, что касается загрузки и извлечения изображений в ASP.NET Core MVC. Если вы хотите реализовать остальную часть операции CRUD — редактирование, вы можете ознакомиться с этой статьей —  ASP.NET Core MVC CRUD с Entity Framework.