13 марта 2011 г.

Работа с изображениями (Uploading with Resize) в Asp.Net MVC

Буквально в каждом проекте приходится реализовывать загрузку картинок пользователем на сервер. Сама загрузка - задача довольно тривиальная, но в реальности нам нужно учитывать множество деталей, влияя таким образом на пользовательский контент. Сегодня мы займемся тем, что реализуем загрузку картинки на сервер, с изменением ее пропорций, если размеры картинки превышают максимально допустимые. Также, заодно, мы будем ограничивать физический размер файла (в килобайтах).


Итак, начнем с того, что создадим папку Uploads, в которую будут попадать все загруженные файлы, и новый статический класс Utils, в котором будет происходить сам ресайз (отдельный класс позволит нам в дальнейшем использовать его функционал из разных мест).
Еще нам необходимо прописать в разделе configuration файла web.config параметр, отвечающий за путь к папке загрузки. Таким образом, путь будет прописан только здесь, что позволит в любой момент его изменить без редактирования кода:

Займемся нашим статическим классом. Он будет содержать 2 константы с максимально допустимыми размерами картинки, и метод SaveImage, входящим параметром которого будет отправленный страничкой файл.


// Максимальные допустимые размеры картинки
const int maxWidth = 400;
const int maxHeight = 600;


   1:  public static void SaveImage(HttpPostedFileBase hpf)
   2:  {
   3:      if (hpf != null && hpf.ContentLength != 0 && hpf.ContentLength <= 307200)
   4:      {
   5:          using (System.Drawing.Bitmap originalPic = 
   6:                      new System.Drawing.Bitmap(hpf.InputStream, false))
   7:          {
   8:              // Вычисление новых размеров картинки
   9:              int width = originalPic.Width; //текущая ширина
  10:              int height = originalPic.Height; //текущая высота
  11:              int widthDiff = (width - maxWidth); //разница с допуст. шириной
  12:              int heightDiff = (height - maxHeight); //разница с допуст. высотой
  13:   
  14:              // Определение размеров, которые необходимо изменять
  15:              bool doWidthResize = (maxWidth > 0 && width > maxWidth &&
  16:                                  widthDiff > -1 && widthDiff > heightDiff);
  17:              bool doHeightResize = (maxHeight > 0 && height > maxHeight &&
  18:                                  heightDiff > -1 && heightDiff > widthDiff);
  19:   
  20:              // Ресайз картинки
  21:              if (doWidthResize || doHeightResize || (width.Equals(height)
  22:                              && widthDiff.Equals(heightDiff)))
  23:              {
  24:                  int iStart;
  25:                  Decimal divider;
  26:                  if (doWidthResize)
  27:                  {
  28:                      iStart = width;
  29:                      divider = Math.Abs((Decimal)iStart / maxWidth);
  30:                      width = maxWidth;
  31:                      height = (int)Math.Round((height / divider));
  32:                  }
  33:                  else
  34:                  {
  35:                      iStart = height;
  36:                      divider = Math.Abs((Decimal)iStart / maxHeight);
  37:                      height = maxHeight;
  38:                      width = (int)Math.Round((width / divider));
  39:                  }
  40:              }
  41:   
  42:              // Сохраняем файл в папку пользователя
  43:              using (System.Drawing.Bitmap newBMP = 
  44:                      new System.Drawing.Bitmap(originalPic, width, height))
  45:              {
  46:                  using (System.Drawing.Graphics oGraphics = 
  47:                              System.Drawing.Graphics.FromImage(newBMP))
  48:                  {
  49:                      oGraphics.SmoothingMode = 
  50:                              System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
  51:                      oGraphics.InterpolationMode = 
  52:           System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
  53:                      oGraphics.DrawImage(originalPic, 0, 0, width, height);
  54:   
  55:                      int idx = hpf.FileName.LastIndexOf('.');
  56:                      string ext = 
  57:                       hpf.FileName.Substring(idx, hpf.FileName.Length - idx);
  58:   
  59:                      // Формируем имя для картинки
  60:                      Random rnd = new Random();
  61:                      int imageName = rnd.Next();
  62:   
  63:                      string filePath = 
  64:                          HttpContext.Current.Server.MapPath(
  65:                          ConfigurationManager.AppSettings["ImagesPath"] +
  66:                              imageName + ext);
  67:   
  68:                      if (System.IO.File.Exists(filePath))
  69:                           System.IO.File.Delete(filePath);
  70:                      newBMP.Save(filePath);
  71:                  }
  72:              }
  73:          }
  74:   
  75:      }
  76:  }

Давайте последовательно пройдемся по всему методу. Вначале, мы проверяем существует ли файл, и не превышает ли его размер (кб) допустимую норму, а дальше все согласно комментариям. В последнем блоке мы формируем новое имя для файла, и сохраняем его по указанному пути, при этом проверяя, не существует ли там случайно такой файл.

Метод для сохранения реализован, но нигде не вызывается. Исправим это. Создадим новое представление Uploader, и реализуем для него методы контроллера.
Первый метод не содержит ничего лишнего, и обрабатывает GET-запросы:


public ActionResult Uploader()
{
return View();
}

Второй метод будет обрабатывать POST-запросы. Именно в нем вызывается метод сохранения картинки:


   1:  [HttpPost]
   2:          public ActionResult Uploader(FormCollection form)
   3:          {
   4:       HttpPostedFileBase hpf = Request.Files["imagefile"] as HttpPostedFileBase;
   5:              Utils.SaveImage(hpf);
   6:   
   7:              return RedirectToAction("uploader");
   8:          }

Где imagefile - атрибут элемента input, который находится на форме и содержит путь к выбранному файлу на компьютере.

Осталось только сделать представление Home/Uploader/. Там у нас будет всего два элемента: input для выбора файла, и кнопка сохранения/загрузки:


<form method="post" enctype="multipart/form-data">
<input type="file" id="imagefile" name="imagefile" /><br />
<input type="submit" value="Save" />
</form>

Вот и все. Можно проверять. Выбранная картинка загружается в нужную папку, и если ее размеры очень велики, то они подгоняется под нужные с сохранением пропорций. Файлы, размером больше 300 кб также не принимаются.

Все работает, но нужно учесть только один момент: мы не сделали проверку формата входящего файла, так что если попытаться загрузить, например, текстовый файл то получим ошибку. Но, я уверен, это можно быстро уладить и самому.
_____
Скачать исходники можно отсюда.