(MVC) MVC (2015 год)

Cropper світлин сайту.

На цій сторінці я розповім про свій кроппер для світлин сайту. Взагалі кроппер має серверну і клієнтську частину. Почнемо з клієнтської частини. Взагалі клєнської частини кропперів існує безліч, но мені більш за все подобається Tapmodo-Jcrop, саме його ви бачите зліва. Клієнтська частина цього кроппера - це jQuery (а взагалі може бути що завгодно, від JAVA, javascript та SilverLight до Flex та Flash). Тут важливо зрозуміти те, що не розуміють "дикі люди", що працюють на PHP. jQuery і javascript - це зовсім інші технології. Тому що дуже часто на сторінці ASP.NET присутній MS AJAX, який не дозволяє працювати jQuery. Точніше дозволяє, як хандлер Sys.WebForms.PageRequestManager.getInstance().add_endRequest - єле коли ви спробуєте їх зростити... єле це окрема тема. На щастя, останній час мої проєкти на ASP.NET MVC, там немає різниці між jQuery і javascript.

Щоб додати кроппер до сторінки потрібно просто додати до сторінки виклик скриптів.



   1:  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js" type="text/javascript"></script>
   2:  <script type="text/javascript" src="../../tapmodo-Jcrop/js/jquery.Jcrop.js"></script>
   3:  <script type="text/javascript">
   4:   
   5:   
   6:      jQuery(function ($) {
   7:   
   8:          var jcrop_api;
   9:   
  10:          $('#target').Jcrop({
  11:              onChange: showCoords,
  12:              onSelect: showCoords,
  13:              onRelease: clearCoords
  14:          }, function () {
  15:              jcrop_api = this;
  16:          });
  17:   
  18:          $('#coords').on('change', 'input', function (e) {
  19:              var x1 = $('#x1').val(),
  20:                  x2 = $('#x2').val(),
  21:                  y1 = $('#y1').val(),
  22:                  y2 = $('#y2').val();
  23:              jcrop_api.setSelect([x1, y1, x2, y2]);
  24:          });
  25:   
  26:          var img = document.getElementById('target');
  27:          $('#imagewidth').val(img.width);
  28:   
  29:      });
  30:   
  31:      // Simple event handler, called from onChange and onSelect
  32:      // event handlers, as per the Jcrop invocation above
  33:      function showCoords(c) {
  34:          $('#x1').val(c.x);
  35:          $('#y1').val(c.y);
  36:          $('#x2').val(c.x2);
  37:          $('#y2').val(c.y2);
  38:          $('#w').val(c.w);
  39:          $('#h').val(c.h);
  40:      };
  41:   
  42:      function clearCoords() {
  43:          $('#coords input').val('');
  44:      };
  45:   
  46:   
  47:   
  48:  </script>


Але додати готовий скриптік - це невеличка частка всієї цієї роботи. Далі на сторінці е ще щось, і про це ми поговоримо детальніше. Сам по собі малюнок тут відтворюється на сторінці хандлером, який викликаеться у 67-й стрічці.



  50:  <h2>Crop Foto</h2>
  51:   
  52:  <%Dim URI As String = "http://" & System.Configuration.ConfigurationManager.AppSettings("HostingURL")%>
  53:   
  54:  <form id="coords" class="coords" method="post" action="<%:URI %><%: Url.Action("Crop" & viewcontext.RouteData.Values("controller"), viewcontext.RouteData.Values("controller"))%>">
  55:   
  56:  <br />
  57:      <div class="inline-labels">
  58:      <label>X1 <input type="text" size="4" id="x1" name="x1" /></label>
  59:      <label>Y1 <input type="text" size="4" id="y1" name="y1" /></label>
  60:      <label>X2 <input type="text" size="4" id="x2" name="x2" /></label>
  61:      <label>Y2 <input type="text" size="4" id="y2" name="y2" /></label>
  62:      <label>W <input type="text" size="4" id="w" name="w" /></label>
  63:      <label>H <input type="text" size="4" id="h" name="h" /></label>
  64:      </div>
  65:  <br />
  66:   
  67:  <img src="<%:URI %>/GetImage.ashx?ID=<%: ViewContext.RouteData.Values("id")%>&Mode=w&w=800" id="target">
  68:   
  69:  <br /><br /><br />
  70:   
  71:  <%: Html.Hidden("id", ViewContext.RouteData.Values("id"))%>
  72:  <%: Html.Hidden("ret", request.QueryString("ret"))%>
  73:  <%: Html.Hidden("imagewidth")%>
  74:  <%: Html.Hidden("securitytoken1", OCMR.SecurityToken.GetToken(Model.id))%>
  75:   
  76:   <input type="submit" id="submit1" value="Crop and Return" name="submit1"  style="width:200px" class="button1" /> 
  77:   </form>
  78:   
  79:   <br /><br />


Все, що робить цей скриптік, ще рахує координати від подій OnMousever і записує нові координати у поля x1,x2,y1,y2,h,w. Даді потрібно передати ці координати на сервер. Я маю стандартний інтерфейс свого графічного двигуна, він вимагає securitytoken1 та інші параметры, які ви бачите у стрічках 71-74. Чому так важно, навіщо це? А тому, що кроппер та інші графічні операції можуть працювати по AJAX без повного постбеку - і тоді усі модифікації та видалення графіки потребують не FORMS-аутентифікації сайту, а securitytoken, так як описано у мене на сторінці Сховище графіки на SQL FileStream та канал браузеру multipart/form-data.

Але ця сторінка у мене працює по повному постбеку, тому MVC-контролер лише транслює виклик до хандлеру, який додає невеличкий wrapper-service до усього мойого графичного двигуна і дозволяє викликати всі графичні функції безпосередньо з серверного контроллеру.





   1:  Imports System.Web
   2:  Imports System.Web.Services
   3:   
   4:  Public Class CropImage
   5:      Implements System.Web.IHttpHandler
   6:   
   7:      'вызов хандлера не напрямую, а из MVC-контроллера
   8:      'Dim CropHandler As New CropImage
   9:      'CropHandler.ProcessRequest(System.Web.HttpContext.Current)
  10:      ''при вызове хандлера из контроллера SecurityToken в принципе не нужен - но если хандлер висит на сайте, чтобы защитить рисунки - токен нужен
  11:   
  12:      Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
  13:          'x1=197&y1=128&x2=359&y2=272&w=162&h=144&id=b6b29e61-ffa8-49ec-902b-b18de2fa6550&ret=http%3a%2f%2flocalhost%3a52796%2fSupervisor%2fModerateFoto%2f05e79a33-9c06-4e48-8f32-70e2c2ace442&imagewidth=800
  14:          'если параметр RET (URL куда надо перейти по окончанию обработки), то не переходим никуда, переходить будет вызывающий код контроллера
  15:   
  16:          Dim PRM = context.Request.Form
  17:          '
  18:          Dim ImageID As Guid
  19:          Dim Ret As String
  20:          'єто непоредственно прилетает от кроппера X1,Y1 - начальная координата с левого верхнего угла
  21:          Dim X1 As Integer
  22:          Dim Y1 As Integer
  23:          Dim H As Integer
  24:          Dim W As Integer
  25:          Dim X2 As Integer
  26:          Dim Y2 As Integer
  27:          'но рисунок для браузера был отмасштабирован к размеру ImageWidth, например 800
  28:          Dim ImageWidth As Integer
  29:          '
  30:          Dim SecurityToken As String = PRM("securitytoken1")
  31:          '
  32:          Try
  33:              Ret = PRM("Ret")
  34:              ImageID = Guid.Parse(PRM("id"))
  35:              '
  36:              X1 = CInt(PRM("x1"))
  37:              Y1 = CInt(PRM("y1"))
  38:              H = CInt(PRM("h"))
  39:              W = CInt(PRM("w"))
  40:              X2 = CInt(PRM("x2"))
  41:              Y2 = CInt(PRM("y2"))
  42:              '
  43:              ImageWidth = CInt(PRM("imagewidth"))
  44:          Catch ex As Exception
  45:              context.Response.ContentType = "text/plain"
  46:              context.Response.Write("bad parm 1")
  47:          End Try
  48:          '
  49:          Dim FS_DB As New OCMR_FSDataContext
  50:          FS_DB.ExecuteCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED")
  51:          FS_DB.CommandTimeout = 0
  52:          '
  53:          'проверили токен
  54:          '
  55:          Dim CheckSecurityToken = FS_DB.DecryptSecurityToken(SecurityToken, ImageID)
  56:          If CheckSecurityToken Is Nothing Then
  57:              context.Response.ContentType = "text/plain"
  58:              context.Response.Write("bad parm 8")
  59:              Exit Sub
  60:          End If
  61:          If CheckSecurityToken <> ImageID Then
  62:              context.Response.ContentType = "text/plain"
  63:              context.Response.Write("bad parm 9")
  64:              Exit Sub
  65:          End If
  66:          '
  67:          'прочитали рисунок
  68:          '
  69:          Dim CurrentFoto = (From X In FS_DB.EventFotos Select X Where X.RowGuid = ImageID).ToList
  70:          If CurrentFoto.Count = 0 Then
  71:              context.Response.ContentType = "text/plain"
  72:              context.Response.Write("no foto")
  73:              Exit Sub
  74:          End If
  75:          'байтовый поток
  76:          Dim ImageBytes(CurrentFoto(0).Image.Length) As Byte
  77:          ImageBytes = CurrentFoto(0).Image.ToArray
  78:          'размерность рисунка
  79:          Dim Dimension As ImageService.Dimension = (New ImageService).GetDimension(ImageBytes)
  80:          Dim WidthRatio As Single = Dimension.Width / 800
  81:          '
  82:          'запомнили имена кешей
  83:          Dim Cache1 As String = CurrentFoto(0).Cache1
  84:          Dim Cache2 As String = CurrentFoto(0).Cache2
  85:          If Cache1 Is Nothing Then Cache1 = ""
  86:          If Cache2 Is Nothing Then Cache2 = ""
  87:          '
  88:          'создали новый рисунок
  89:          '
  90:          Dim NewImage As Byte() = ImageService.Crop(ImageBytes, CInt(X1 * WidthRatio), CInt(Y1 * WidthRatio), CInt(W * WidthRatio), CInt(H * WidthRatio))
  91:          '
  92:          'Теперь сохрании в базу новый FileStream
  93:          '
  94:          CurrentFoto(0).Image = NewImage
  95:          CurrentFoto(0).Heigth = Dimension.Heigth
  96:          CurrentFoto(0).Width = Dimension.Width
  97:          FS_DB.SubmitChanges()
  98:          '
  99:          'теперь создаем новые кеши
 100:          '
 101:          Dim LastID As Integer = CurrentFoto(0).i
 102:          Dimension = (New ImageService).GetDimension(NewImage)
 103:          Dim Y As New LoadImage
 104:          Y.CreateCache(context, NewImage, Dimension, LastID)
 105:          '
 106:          'удаляем старые кеши до поворота
 107:          '
 108:          Dim Dir1 As String = System.Configuration.ConfigurationManager.AppSettings("ImageCachePatch")
 109:          Dim FullFileName1 = IO.Path.Combine(Dir1, Cache1)
 110:          If My.Computer.FileSystem.FileExists(FullFileName1) Then
 111:              My.Computer.FileSystem.DeleteFile(FullFileName1)
 112:          End If
 113:          Dim FullFileName2 = IO.Path.Combine(Dir1, Cache2)
 114:          If My.Computer.FileSystem.FileExists(FullFileName2) Then
 115:              My.Computer.FileSystem.DeleteFile(FullFileName2)
 116:          End If
 117:          '
 118:          If Ret <> "" Then
 119:              'перешли на заданную страничку
 120:              context.Response.Redirect(Ret)
 121:          Else
 122:              'AJAX mode -  ответили в браузер
 123:              context.Response.ContentType = "image/bmp"
 124:              context.Response.BinaryWrite(NewImage)
 125:          End If
 126:   
 127:   
 128:          Exit Sub
 129:   
 130:      End Sub
 131:   
 132:      ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
 133:          Get
 134:              Return False
 135:          End Get
 136:      End Property
 137:   
 138:  End Class


У стрічці 90 викликається Загально графічні функції, які і формують світлину нового розміру. Взаємодія контроллеру і хандлеру здається трошки важкою, але якщо зрозуміти, що хандлер може працювати окремо від AJAX-викликів, то все стає на місце.



Comments ( )
<00>  <01>  <02>  <03>  <04>  <05>  <06>  <07>  <08>  <09>  <10>  <11>  <12>  <13>  <14>  <15>  <16>  <17>  <18>  <19>  <20>  <21>  <22>  <23
Link to this page: //www.vb-net.com/Cropper/index.htm
<SITEMAP>  <MVC>  <ASP>  <NET>  <DATA>  <KIOSK>  <FLEX>  <SQL>  <NOTES>  <LINUX>  <MONO>  <FREEWARE>  <DOCS>  <ENG>  <CHAT ME>  <ABOUT ME>  < THANKS ME>