Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7
Рассмотрим, как загружать файлы на сервер в 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
преобразовать массив байтов
в нужный формат для вывода изображения.
В итоге все будет выглядеть примерно так:
Далее изменим контроллер 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 считывается в массив байтов, который мы затем можем сохранить в базу данных.