Загрузка файлов на сервер

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7

Последнее обновление: 29.12.2019

Рассмотрим, как загружать файлы на сервер в ASP.NET Core. Все загружаемые файлы в ASP.NET Core представлены типом IFormFile из пространства имен Microsoft.AspNetCore.Http. Соответственно для получения отправленного файла в контроллере необходимо использовать IFormFile. Затем с помощью методов IFormFile мы можем произвести различные манипуляции файлом - получит его свойства, сохранить, получить его поток и т.д. Некоторые его свойства и методы:

  • ContentType: тип файла

  • FileName: название файла

  • Length: размер файла

  • CopyTo/CopyToAsync: копирует файл в поток

  • OpenReadStream: открывает поток файла для чтения

При работе с файлами прежде всего следует выбрать способ сохранения файлов. Существует два способа - в бд и в файловой системе. В данном случае рассмотрим оба способа на примере проекта ASP.NET Core MVC.

Сохранение в файловой системе

Определим простую модель FileModel:

public class FileModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Path { get; set; }
}

Свойство Name здесь хранит имя файла, а свойство Path - путь к файлу в файловой системе.

Определим контекст данных ApplicationContext:

using Microsoft.EntityFrameworkCore;

namespace FileUploadApp.Models
{
    public class ApplicationContext : DbContext
    {
        public DbSet<FileModel> Files { get; set; }
        public ApplicationContext(DbContextOptions<ApplicationContext> options)
            : base(options)
        {
            Database.EnsureCreated();
        }
    }
}

Для хранения файлов создадим в проекте в каталоге wwwroot папку Files.

Далее определим в контроллере методы, которые будет получать и выводить полученные файлы в виде картинок (предполагается, что загружаются изображения):

using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using FileUploadApp.Models;
using System.IO;
using Microsoft.AspNetCore.Hosting;

namespace FileUploadApp.Controllers
{
    public class HomeController : Controller
    {
        ApplicationContext _context;
        IWebHostEnvironment _appEnvironment;

        public HomeController(ApplicationContext context, IWebHostEnvironment appEnvironment)
        {
            _context = context;
            _appEnvironment = appEnvironment;
        }

        public IActionResult Index()
        {
            return View(_context.Files.ToList());
        }
        [HttpPost]
        public async Task<IActionResult> AddFile(IFormFile uploadedFile)
        {
            if (uploadedFile != null)
            {
                // путь к папке Files
                string path = "/Files/" + uploadedFile.FileName;
                // сохраняем файл в папку Files в каталоге wwwroot
                using (var fileStream = new FileStream(_appEnvironment.WebRootPath + path, FileMode.Create))
                {
                    await uploadedFile.CopyToAsync(fileStream);
                }
                FileModel file = new FileModel { Name = uploadedFile.FileName, Path = path };
                _context.Files.Add(file);
                _context.SaveChanges();
            }
           
            return RedirectToAction("Index");
        }
    }
}

Для получения полного пути к каталогу wwwroot применяется свойство WebRootPath объекта IWebHostEnvironment. Для копирования файла в папку Files создается поток FileStream, в который записывается файл с помощью метода CopyToAsync.

Затем изменим представление Index.cshtml:

@model IEnumerable<FileUploadApp.Models.FileModel>
@{
    ViewData["Title"] = "Добавление файла";
}
<h3>Выберите файл для загрузки</h3>
<form asp-action="AddFile" asp-controller="Home" method="post" enctype="multipart/form-data">
    <input type="file" name="uploadedFile" /><br>
    <input type="submit" value="Загрузить" />
</form>

<h3>Все файлы</h3>
@foreach(var f in Model)
{
    <p><img src="@Url.Content(f.Path)"></p>
}

Благодаря установке атрибута формы enctype="multipart/form-data" браузер будет знать, что вместе с формой надо передать файл.

После формы идет отображение файлов с помощью тега img на веб-странице.

Множественная загрузка файлов

При множественной загрузке файлов создаем в представлении набор элементов с типом file с одинаковым значением атрибута name:

<form asp-action="AddFile" asp-controller="Home" method="post" enctype="multipart/form-data">
    <input type="file" name="uploads" /><br>
    <input type="file" name="uploads" /><br>
    <input type="file" name="uploads" /><br>
    <input type="submit" value="Загрузить" />
</form>

Теперь в метода контроллера мы уже будем получать коллекцию объектов IFormFile - IFormFileCollection:

[HttpPost]
public async Task<IActionResult> AddFile(IFormFileCollection uploads)
{
    foreach(var uploadedFile in uploads)
    {
        // путь к папке Files
        string path = "/Files/" + uploadedFile.FileName;
        // сохраняем файл в папку Files в каталоге wwwroot
        using (var fileStream = new FileStream(_appEnvironment.WebRootPath + path, FileMode.Create))
        {
            await uploadedFile.CopyToAsync(fileStream);
        }
        FileModel file = new FileModel { Name = uploadedFile.FileName, Path = path };
        _context.Files.Add(file);
    }
     _context.SaveChanges();

    return RedirectToAction("Index");
}

Загрузка в базу данных

Пусть у нас есть следующая модель Person:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public byte[] Avatar { get; set; }
}

Для простоты примера в модели Person всего три свойства: id, имя пользователя и массив байтов файла, который будет храниться в бд и который будет представлять аватар пользователя. В базе данных свойство Avatar будет сопоставляться со столбцом с типом varbinary(max).

Пусть у нас будет следующий контекст данных FilesContext:

public class FilesContext : DbContext
{
    public DbSet<Person> People { get; set; }
    public FilesContext(DbContextOptions<FilesContext> options)
        : base(options)
    { 
		Database.EnsureCreated();
	}
}

Для создания модели Person определим вспомогательную модель PersonViewModel:

using Microsoft.AspNetCore.Http;
public class PersonViewModel
{
    public string Name { get; set; }
    public IFormFile Avatar { get; set; }
}

Далее определим следующее представление Index.cshtml:

@model IEnumerable<FileUploadApp.Models.Person>

@{
    ViewBag.Title = "Добавление пользователя";
}
<form asp-action="Create" asp-controller="Home" method="post" enctype="multipart/form-data">
    <p>
        <label>Имя</label>
        <input name="Name" class="form-control" />
    </p>
    <p>
        <label>Аватар</label>
        <input name="Avatar" type="file" class="form-control" />
    </p>
    <p>
        <input type="submit" value="Добавить" />
    </p>
</form>
<h3>Все пользователи</h3>
@foreach (var p in Model)
{
    <div>
        <h4>@p.Name</h4>
        @if (p.Avatar != null)
        {
            <img style='width:80px; height:60px;' src="data:image/jpeg;base64,@(Convert.ToBase64String(p.Avatar))" />
        }
    </div>
}

Опять же для упрощения примера одно и то же представление будет выводить форму для добавления нового объекта и список имеющихся объектов.

При выводе списка объектов, если файл представляет изображение, то мы можем с помощью метода Convert.ToBase64String преобразовать массив байтов в нужный формат для вывода изображения.

В итоге все будет выглядеть примерно так:

Upload files in ASP.NET Core

Далее изменим контроллер HomeController:

using Microsoft.AspNetCore.Mvc;
using System.Linq;
using FileUploadApp.Models;
using System.IO;

namespace FileUploadApp.Controllers
{
    public class HomeController : Controller
    {
        FilesContext _context;

        public HomeController(FilesContext context)
        {
            _context = context;
        }
        public IActionResult Index()
        {
            return View(_context.People.ToList());
        }

        [HttpPost]
        public IActionResult Create(PersonViewModel pvm)
        {
            Person person = new Person { Name = pvm.Name };
            if (pvm.Avatar != null)
            {
                byte[] imageData = null;
                // считываем переданный файл в массив байтов
                using (var binaryReader = new BinaryReader(pvm.Avatar.OpenReadStream()))
                {
                    imageData = binaryReader.ReadBytes((int)pvm.Avatar.Length);
                }
                // установка массива байтов
                person.Avatar = imageData;
            }
            _context.People.Add(person);
            _context.SaveChanges();

            return RedirectToAction("Index");
        }
    }
}

В данном случае объект IFormFile с помощью класса BinaryReader считывается в массив байтов, который мы затем можем сохранить в базу данных.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850