1+ using System ;
2+ using System . Drawing . Imaging ;
3+ using System . IO ;
4+ using System . Linq ;
5+ using System . Security . Cryptography ;
6+ using System . Text ;
7+ using Funq ;
8+ using ServiceStack . Common ;
9+ using ServiceStack . Common . Utils ;
10+ using ServiceStack . Common . Web ;
11+ using ServiceStack . ServiceHost ;
12+ using ServiceStack . ServiceInterface ;
13+ using ServiceStack . Text ;
14+ using ServiceStack . WebHost . Endpoints ;
15+ using System . Drawing ;
16+ using System . Drawing . Drawing2D ;
17+
18+ //Entire C# source code for ImageResizer backend - there is no other .cs :)
19+ namespace ImageResizer
20+ {
21+ [ Route ( "/upload" ) ]
22+ public class Upload
23+ {
24+ public string Url { get ; set ; }
25+ }
26+
27+ [ Route ( "/images" ) ]
28+ public class Images { }
29+
30+ [ Route ( "/resize/{Id}" ) ]
31+ public class Resize
32+ {
33+ public string Id { get ; set ; }
34+ public string Size { get ; set ; }
35+ }
36+
37+ public class ImageService : Service
38+ {
39+ const int ThumbnailSize = 100 ;
40+ readonly string UploadsDir = "~/uploads" . MapHostAbsolutePath ( ) ;
41+ readonly string ThumbnailsDir = "~/uploads/thumbnails" . MapHostAbsolutePath ( ) ;
42+
43+ public object Get ( Images request )
44+ {
45+ return Directory . GetFiles ( UploadsDir ) . SafeConvertAll ( x => x . SplitOnLast ( Path . DirectorySeparatorChar ) . Last ( ) ) ;
46+ }
47+
48+ public object Post ( Upload request )
49+ {
50+ if ( request . Url != null )
51+ {
52+ using ( var ms = new MemoryStream ( request . Url . GetBytesFromUrl ( ) ) )
53+ {
54+ WriteImage ( ms ) ;
55+ }
56+ }
57+
58+ foreach ( var uploadedFile in RequestContext . Files . Where ( uploadedFile => uploadedFile . ContentLength > 0 ) )
59+ {
60+ using ( var ms = new MemoryStream ( ) )
61+ {
62+ uploadedFile . WriteTo ( ms ) ;
63+ WriteImage ( ms ) ;
64+ }
65+ }
66+
67+ return HttpResult . Redirect ( "/" ) ;
68+ }
69+
70+ private void WriteImage ( Stream ms )
71+ {
72+ var hash = GetMd5Hash ( ms ) ;
73+
74+ ms . Position = 0 ;
75+ var fileName = hash + ".png" ;
76+ using ( var img = Image . FromStream ( ms ) )
77+ {
78+ img . Save ( UploadsDir . CombineWith ( fileName ) ) ;
79+
80+ var stream = Resize ( img , ThumbnailSize , ThumbnailSize ) ;
81+ File . WriteAllBytes ( ThumbnailsDir . CombineWith ( fileName ) , stream . ReadFully ( ) ) ;
82+ }
83+ }
84+
85+ [ AddHeader ( ContentType = "image/jpg" ) ]
86+ public object Get ( Resize request )
87+ {
88+ var imagePath = UploadsDir . CombineWith ( request . Id + ".png" ) ;
89+ if ( request . Id == null || ! File . Exists ( imagePath ) )
90+ throw HttpError . NotFound ( request . Id + " was not found" ) ;
91+
92+ using ( var stream = File . OpenRead ( imagePath ) )
93+ using ( var img = Image . FromStream ( stream ) )
94+ {
95+
96+ var parts = request . Size == null ? null : request . Size . Split ( 'x' ) ;
97+ int width = img . Width ;
98+ int height = img . Height ;
99+
100+ if ( parts != null && parts . Length > 0 )
101+ int . TryParse ( parts [ 0 ] , out width ) ;
102+
103+ if ( parts != null && parts . Length > 1 )
104+ int . TryParse ( parts [ 1 ] , out height ) ;
105+
106+ return Resize ( img , width , height ) ;
107+ }
108+ }
109+
110+ public static string GetMd5Hash ( Stream stream )
111+ {
112+ var hash = MD5 . Create ( ) . ComputeHash ( stream ) ;
113+ var sb = new StringBuilder ( ) ;
114+ for ( var i = 0 ; i < hash . Length ; i ++ )
115+ {
116+ sb . Append ( hash [ i ] . ToString ( "x2" ) ) ;
117+ }
118+ return sb . ToString ( ) ;
119+ }
120+
121+ public static Stream Resize ( Image img , int newWidth , int newHeight )
122+ {
123+ if ( newWidth != img . Width || newHeight != img . Height )
124+ {
125+ var ratioX = ( double ) newWidth / img . Width ;
126+ var ratioY = ( double ) newHeight / img . Height ;
127+ var ratio = Math . Max ( ratioX , ratioY ) ;
128+
129+ var width = ( int ) ( img . Width * ratio ) ;
130+ var height = ( int ) ( img . Height * ratio ) ;
131+
132+ var newImage = new Bitmap ( width , height ) ;
133+ Graphics . FromImage ( newImage ) . DrawImage ( img , 0 , 0 , width , height ) ;
134+ img = newImage ;
135+
136+ if ( img . Width != newWidth || img . Height != newHeight )
137+ {
138+ var startX = ( Math . Max ( img . Width , newWidth ) - Math . Min ( img . Width , newWidth ) ) / 2 ;
139+ var startY = ( Math . Max ( img . Height , newHeight ) - Math . Min ( img . Height , newHeight ) ) / 2 ;
140+ img = Crop ( img , newWidth , newHeight , startX , startY ) ;
141+ }
142+ }
143+
144+ var ms = new MemoryStream ( ) ;
145+ img . Save ( ms , ImageFormat . Png ) ;
146+ ms . Position = 0 ;
147+ return ms ;
148+ }
149+
150+ public static Image Crop ( Image Image , int newWidth , int newHeight , int startX = 0 , int startY = 0 )
151+ {
152+ if ( Image . Height < newHeight )
153+ newHeight = Image . Height ;
154+
155+ if ( Image . Width < newWidth )
156+ newWidth = Image . Width ;
157+
158+ using ( var bmp = new Bitmap ( newWidth , newHeight , PixelFormat . Format24bppRgb ) )
159+ {
160+ bmp . SetResolution ( 72 , 72 ) ;
161+ using ( var g = Graphics . FromImage ( bmp ) )
162+ {
163+ g . SmoothingMode = SmoothingMode . AntiAlias ;
164+ g . InterpolationMode = InterpolationMode . HighQualityBicubic ;
165+ g . PixelOffsetMode = PixelOffsetMode . HighQuality ;
166+ g . DrawImage ( Image , new Rectangle ( 0 , 0 , newWidth , newHeight ) , startX , startY , newWidth , newHeight , GraphicsUnit . Pixel ) ;
167+
168+ using ( var ms = new MemoryStream ( ) )
169+ {
170+ bmp . Save ( ms , ImageFormat . Png ) ;
171+ Image . Dispose ( ) ;
172+ var outimage = Image . FromStream ( ms ) ;
173+ return outimage ;
174+ }
175+ }
176+ }
177+ }
178+ }
179+
180+ public class AppHost : AppHostBase
181+ {
182+ public AppHost ( ) : base ( "Image Resizer" , typeof ( AppHost ) . Assembly ) { }
183+ public override void Configure ( Container container ) { }
184+ }
185+
186+ public class Global : System . Web . HttpApplication
187+ {
188+ protected void Application_Start ( object sender , EventArgs e )
189+ {
190+ new AppHost ( ) . Init ( ) ;
191+ }
192+ }
193+ }
0 commit comments