Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core
Большинство веб-приложений так или иначе используют файлы: аудио, видео, изображения, текстовые и другие типы файлов. В этой статье рассмотрим, как сохранять файлы (на примере файлов изображений) в базе данных.
Но сразу надо сказать, что есть два способа сохранения файлов - в бд и в файловой системе, и хранение файлов в базе данных - не всегда является идеальным способом. Microsoft рекомендует сохранять в базе данных файлы, размер которых не более 1 мб. Все остальное - в файловой системе. Подробное исследование на эту тему: To BLOB or Not To BLOB
Так или иначе у обоих способов есть свои плюсы. Например, при хранении в бд соблюдается целостность данных. В то же время работа с базой данных - это одно из узких мест веб-приложения. И чем больше запросы к бд будут оптимизированы, и чем их будет меньше, тем больше будет производительность. А доступ к файловой системе, как правило, будет происходить быстрее, чем доступ к бд. И если у вас веб-приложение активно работает с большими файлами, как например, социальная сеть вконтакте, то лучше их сохранять в файловой системе, а в базе данных хранить ссылки.
Итак, создадим новый проект приложения ASP.NET MVC и добавим в него следующую модель Picture, которая будет описывать загружаемое изображение:
public class Picture { public int Id { get; set; } public string Name { get; set; } // название картинки public byte[] Image { get; set; } }
Для простоты примера тут всего три свойства: id, название изображения и массив байтов файла, который будет храниться в бд. И контекст данных тоже будет выглядеть просто:
public class PictureContext : DbContext { public PictureContext() : base("DefaultConnection") { } public DbSet<Picture> Pictures { get; set; } }
В данном случае будем использовать строку по умолчанию DefaultConnection, которая уже определена в файле web.config. Для взаимодействия с БД мы будем использовать подход Code First. Но в случае если у наc есть уже бд, то нам нужна соответствующая таблица Pictures с тремя полями, где поле Image будет иметь тип varbinary(max).
Теперь перейдем к контроллеру. В нем определим всего два действия - для добавления картинок и для их просмотра в виде списка:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using ImageDbApp.Models; using System.IO; namespace ImageDbApp.Controllers { public class HomeController : Controller { PictureContext db = new PictureContext(); public ActionResult Index() { return View(db.Pictures); } public ActionResult Create() { return View(); } [HttpPost] public ActionResult Create(Picture pic, HttpPostedFileBase uploadImage) { if (ModelState.IsValid && uploadImage!=null) { byte[] imageData = null; // считываем переданный файл в массив байтов using (var binaryReader = new BinaryReader(uploadImage.InputStream)) { imageData = binaryReader.ReadBytes(uploadImage.ContentLength); } // установка массива байтов pic.Image = imageData; db.Pictures.Add(pic); db.SaveChanges(); return RedirectToAction("Index"); } return View(pic); } } }
Представление Index для вывода изображений из бд:
@model IEnumerable<ImageDbApp.Models.Picture> @{ ViewBag.Title = "Index"; } <h2>Мои изображения</h2> <p> @Html.ActionLink("Добавить изображение", "Create") </p> <table> <tr> <th>Название</th> <th>Картинка</th> </tr> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.Name) </td> <td> @Html.Raw("<img style='width:80px; height:60px;' src=\"data:image/jpeg;base64," + Convert.ToBase64String(item.Image) + "\" />") </td> </tr> } </table>
Так как свойство item.Image у нас представляет массив байтов, то мы можем переконвертировать его в строку base64 и таким образом вывести
в представлении. Если же у нас в бд храняться какие-либо другие файлы, не обязательно файлы изображений, то мы можем просто повесить ссылку на них. Например,
<a href="Home/GetFile/@item.Id">Загрузить</a>
, где GetFile - это название действия, которое по id будет извлекать файл
из бд и возвращать объект FileContentResult.
В итоге это могло бы выглядеть примерно так:
И представляение Create для создания объекта данной модели:
@model ImageDbApp.Models.Picture @{ ViewBag.Title = "Create"; } @using (Html.BeginForm("Create", "Home", FormMethod.Post, new { enctype = "multipart/form-data" })) { @Html.ValidationSummary(true) <fieldset> <legend>Добавить изображение</legend> <div class="editor-label">Название</div> <div class="editor-field"> @Html.EditorFor(model => model.Name) @Html.ValidationMessageFor(model => model.Name) </div> <div class="editor-label">Файл изображения</div> <div class="editor-field"> <input type="file" name="uploadImage" /> </div> <p> <input type="submit" value="Добавить" /> </p> </fieldset> } <div> @Html.ActionLink("Вернуться к списку", "Index") </div> @section Scripts { @Scripts.Render("~/bundles/jqueryval") }