Compress/Resize images before and after upload in Asp.net

Introduction

Today we will cover scenarios to compress and resize images before and after upload in Asp.net. Specifically, sometimes users are in low bandwidth area where cellular network coverage is low and they are trying to upload images for their business flows. We’ll also resize images on the server side and also compress images further by reducing the quality to optimize server side storage.

Goal

  1. Resize large images to smaller sizes before upload to improve upload speed.
  2. Resize images on the server side if still required.
  3. Reduce the quality of JPG images on server side to further optimize storage space on the server.
  4. One important focus area was to integrate this functionality into legacy asp.net sites which may or may not use AjaxControlToolkit.

Pre-requisites

I’m targeting legacy Asp.net Web forms application environment. But you can easily modify the code to suit Asp.net MVC code and use jQuery to upload images.

  1. Any Visual Studio environment which supports Web Forms
  2. Install AjaxControlToolkit using NuGet package manager or from DevExpress as an installer. This toolkit since we will be using AsyncFileUpload control.
  3. jQuery latest version

Code: Asp.net Web Forms way

  1. On an empty Web form, drop the AsycFileUpload control
  2. Open the .aspx file and the control details should look like this
<cc1:AsyncFileUpload OnClientUploadError="uploadError" Width="200px"
	OnClientUploadComplete="uploadComplete" runat="server"
	ID="AsyncFileUpload1"
	OnClientUploadStarted="uploadStart"
	accept="image/*"></cc1:AsyncFileUpload>

3. Focus on the OnClientUploadStarted event. We will be calling uploadStart JS function to resize the image here. In case of your Asp.net MVC code, you can drop a regular File control and assign a JS function in the onchange event.

function uploadStart(sender, args) {
        var file = sender._inputFile.files[0];
        resizeAndUpload(file);
}

Javascript Section

function resizeAndUpload(file) {
	var reader = new FileReader();
	reader.onloadend = function () {

	    var tempImg = new Image();
	    tempImg.src = reader.result;
	    tempImg.onload = function () {
		var MAX_WIDTH = 1440; // you can keep any width you want
		var MAX_HEIGHT = 960;  // you can keep any height you want

		var tempW = tempImg.width;
		var tempH = tempImg.height;
                
                //calculating the ratio of width that needs to be resized.
                //resizing the height in the same ratio as width.  
		if (tempW > MAX_WIDTH) {
		        var ratio = tempW / MAX_WIDTH;
			tempW = parseInt(tempW / ratio);
			tempH = parseInt(tempH / ratio);
		}
		else {
		        if (tempH > MAX_HEIGHT) {
			        var ratio = tempW / MAX_HEIGHT;
				tempW = parseInt(tempW / ratio);
				tempH = parseInt(tempH / ratio);
			}
		}

		var canvas = document.createElement('canvas');
		canvas.width = tempW;
		canvas.height = tempH;
		var ctx = canvas.getContext("2d");
		ctx.drawImage(this, 0, 0, tempW, tempH);
		var dataURL = canvas.toDataURL("image/jpeg");

		$.ajax({
			url: "http://<<Server address>>/api/images",
			type: "POST",
			data: 'image=' + dataURL,
			contentType: "application/x-www-form-urlencoded",
			success: function (data, textStatus, jqXHR) {
				console.log('posted image');
			},
			error: function (jqXHR, textStatus, errorThrown) {
				console.log('error posting image');
			}
		});
		
	    }
	}
	reader.readAsDataURL(file);	
}

If you console.log your Base64 string [var dataURL], it will look like (extremely large):

data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDA .... VmaK0v//Z

Web API Code – Refer this section to check how to read the Base64 string in Web API code and save it as an image.

Fun fact: Another important parameter for the method in the line also controls the quality of the image. It is the encoderOptions parameter. Refer this line – canvas.toDataURL("image/jpeg");.

You can write canvas.toDataURL("image/jpeg", 0.5); You can write a value anywhere between 0.1 and 1.0. So 0.5 will give you medium quality, 0.1 will give you lowest quality, 0.7 will give you higher quality and 1.0 will give best quality.

Implementing this parameter will be like a double whammy for the image. Resizing will reduce the size and the second parameter will reduce the quality which will again reduce the image size before upload. Even if you put 0.5, the loss in image quality is generally not clearly visible.

Server side code – Independent code

Note: This server side code is not related to AsyncFileUpload control and it just a server side explanation of resizing and compression on server side.

..
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
...
///
.
code
.
///

public void FileUploadComplete()
{
    // to reduce image quality to further reduce the image size
	// set the compression parameters via EncoderParameter
	// https://docs.microsoft.com/en-us/dotnet/framework/winforms/advanced/how-to-set-jpeg-compression-level
	ImageCodecInfo jpgEncoder = GetEncoder(ImageFormat.Jpeg);

	System.Drawing.Imaging.Encoder myEncoder = System.Drawing.Imaging.Encoder.Quality;
	Int64 compressionPercentage = 50L;

	EncoderParameters myEncoderParameters = new EncoderParameters(1);
	EncoderParameter myEncoderParameter = new EncoderParameter(myEncoder, compressionPercentage);
	myEncoderParameters.Param[0] = myEncoderParameter;

	int width = 1440;
	int height = 960;
        string filename = HttpContext.Current.Server.MapPath("any server physical path") + System.IO.Path.GetFileName("<<Full Path of file name>>");

	using (FileStream fs = new System.IO.FileStream(fileName, System.IO.FileMode.Create))
	{
		System.Drawing.Image image = System.Drawing.Image.FromStream(fs);
		Bitmap bmp = new Bitmap(image);
		Bitmap resultImage = Resize(image, width, height);            
		string imagePath = "<<New Path>>";
		resultImage.Save(imagePath, jpgEncoder, myEncoderParameters);
	}
}

//http://stackoverflow.com/questions/11137979/image-resizing-using-c-sharp
public static Bitmap Resize(Image image, int width, int height)
{
	var destRect = new Rectangle(0, 0, width, height);
	var destImage = new Bitmap(width, height);

	destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);

	using (var graphics = Graphics.FromImage(destImage))
	{
		graphics.CompositingMode = CompositingMode.SourceCopy;
		graphics.CompositingQuality = CompositingQuality.HighQuality;
		graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
		graphics.SmoothingMode = SmoothingMode.HighQuality;
		graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;

		using (var wrapMode = new ImageAttributes())
		{
			wrapMode.SetWrapMode(WrapMode.TileFlipXY);
			graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
		}
	}

	return destImage;
}

private ImageCodecInfo GetEncoder(ImageFormat format)
{
	ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();
	foreach (ImageCodecInfo codec in codecs)
	{
		if (codec.FormatID == format.Guid)
		{
			return codec;
		}
	}
	return null;
}

Points for Server side code

  1. Check the line – Int64 compressionPercentage = 50L;. This reduces the quality of the image by 50 percentage without changing its dimensions. Uptil 50%, there is no visible degradation in the image quality. If you reduce this number to 40 or 25, the quality loss will be clearly visible. So 50% appears to be a sweet spot!. Increasing the number will improve the image quality and vice versa.
  2. The encoder properties are applied while saving the image – resultImage.Save(imagePath, jpgEncoder, myEncoderParameters)
  3. Dimensions can further be changed by Resize method.
  4. The Resize method shows what all image parameters you can change for an image. Please keep in mind that JPG has most of the options supported. It may happen that PNG, TIFF and other formats may not support all the options. E.g. PNG format does not support CompressionPercentage EncoderParameters. Please thoroughly test for other image formats.
  5. PNG format has bits per pixel as parameter of image quality. By default it stores 32 bits per pixel (pixel depth of 32) which is very high quality. You can compress a PNG image by reducing its pixel depth to 24 or 16 or 8 bits per pixel. You can also implement interlacing to compress your PNG. But reducing pixel depth is a completely different ball game. Instead I would suggest code that you covert a PNG to JPG and then follow the code given here. The !faint_hearted can proceed here to compress PNG files.
  6. The System.Drawing library supports reducing the pixel depth for TIFF image formats.

Alternate code: jQuery and Server side code

If you are into jQuery/MVC pages, you can upload the Base64 data via a simple Ajax call.

// Simple HTML File input control
<input type="file" id="fileUpload" name="fileUpload" onchange="onChange()"  accept="image/*" />

// change event of the control
function onChange() {
            debugger;
            var file = $('#fileUpload').prop('files');
            resizeAndUpload(file[0]);
            console.log("called fileUpload");
        }

// You can call the same resizeAndUpload. It also has code that will upload the file data asynchronously.

Note: There is no need to convert the Base64 string to blob data to upload it on to a Web API. Just the correct contentType is sufficient.

The Web API project

In Visual Studio, create a new Web API project. Ensure that you add Microsoft.Aspnet.Cors Nuget package to your Web Api project. In my case the version was 5.2.7. This step will enable your Web API to accept cross-domain calls since generally your Application and Web API will be deployed on different machines or port numbers.

Secondly, in your WebApiConfig.cs file, add this line config.EnableCors(); at the bottom of the Register method.

Observe how I have enabled CORS on the controller class. [EnableCors(origins: "*", headers: "*", methods: "*")].

Important note: Please be very careful when you enable ‘*’ for all origins, headers and method. This is the most insecure method and will allow your Web API to accepts calls from any origin/method/header. Just doing it here for the sake of demo to make things easier for the Ajax call to upload Base64 data via Web API call.

[EnableCors(origins: "*", headers: "*", methods: "*")]
    public class ImagesController : ApiController
    {
        // POST api/values
        public string Post()
        {            
            string result = Request.Content.ReadAsStringAsync().Result;
            byte[] bytes = Convert.FromBase64String(result.Split(new string[] { "base64," }, StringSplitOptions.RemoveEmptyEntries)[1]);

            Image image;
            using (MemoryStream ms = new MemoryStream(bytes))
            {
                image = Image.FromStream(ms);
                Bitmap bmp = new Bitmap(image);
                bmp.Save(HostingEnvironment.MapPath("~/uploads/wow.jpg"), ImageFormat.Jpeg);
            }

            string ServerBaseUrl = HttpContext.Current.Request.Url.Scheme + "://" + HttpContext.Current.Request.Url.Authority + HttpContext.Current.Request.ApplicationPath.TrimEnd('/');
            return ServerBaseUrl + "/uploads/wow.jpg";
        }

    }

Check how the Base64 data is converted to a byte array. In the using section, check how the byte array is loaded in to a MemoryStream, converted to a Bitmap image and finally saving it in a JPG format. You can further resize and compress the image by referring the code in the Server side section.

Important Note: If you do not load the image into a Bitmap and directly try to save the Image to a drive path, you will get a A generic error occurred in GDI+. Loading the image in a Bitmap helped me get rid of this “highly friendly” error message. There are several reasons for this error message but my case was different. I haven’t “Disposed” off objects for the sake of brevity.

The final line returns the full path of the image including server and port name. This can be used at the client side to show the uploaded image. You will need to add a reference to System.Web to make this line work.

Acknowledgements

Many blogs helped write code or gave valuable information. Here they are:

  1. A code sample
  2. A dev from Mozilla.
  3. Also this and this.

Conclusion

Well, we covered scenarios to compress and resize images before and after upload in Asp.net. We also learnt the server side image resize and compression logic. Finally we also saw how to apply the same code in jQuery and Web API scenarios. Hope this helps you. If you have questions, fire a mail to code@onezeroeight.co

1,052 Responses

  1. learn more says:

    Hello, i think that i saw you visited my blog thus i came to “return the favor”.I’m attempting to find things to enhance my website!I suppose its ok to use some of your ideas!!

  2. visit says:

    Its like you read my mind! You appear to know a lot about this, like you wrote the book in it or something. I think that you can do with some pics to drive the message home a bit, but instead of that, this is wonderful blog. A great read. I’ll definitely be back.

  3. Read More says:

    Hiya, I am really glad I have found this info. Nowadays bloggers publish just about gossips and internet and this is actually irritating. A good site with interesting content, this is what I need. Thank you for keeping this web site, I’ll be visiting it. Do you do newsletters? Can not find it.

  4. Homepage says:

    You completed a few nice points there. I did a search on the theme and found nearly all persons will consent with your blog.

  5. certainly like your web site however you need to test the spelling on quite a few of your posts. Many of them are rife with spelling issues and I in finding it very troublesome to inform the truth then again I¡¦ll certainly come again again.

  6. Go Here says:

    I don’t even know how I ended up here, but I thought this post was great. I don’t know who you are but certainly you are going to a famous blogger if you aren’t already 😉 Cheers!

  7. Hey, you used to write fantastic, but the last several posts have been kinda boring¡K I miss your tremendous writings. Past several posts are just a little out of track! come on!

  8. Click Here says:

    Hello there, just became alert to your blog through Google, and found that it is truly informative. I am gonna watch out for brussels. I’ll be grateful if you continue this in future. Lots of people will be benefited from your writing. Cheers!

  9. Keep functioning ,remarkable job!

  10. I’m still learning from you, but I’m trying to reach my goals. I absolutely enjoy reading all that is posted on your blog.Keep the stories coming. I liked it!

  11. Nice post. I was checking constantly this blog and I am impressed! Very useful info specifically the last part 🙂 I care for such info a lot. I was looking for this particular info for a long time. Thank you and good luck.

  12. visit says:

    Howdy very cool site!! Man .. Excellent .. Wonderful .. I’ll bookmark your web site and take the feeds also¡KI am glad to search out numerous useful info here within the post, we want work out extra strategies on this regard, thanks for sharing. . . . . .

  13. website says:

    Normally I do not learn post on blogs, however I wish to say that this write-up very forced me to take a look at and do so! Your writing style has been amazed me. Thanks, quite great article.

  14. click here says:

    Thanks for the sensible critique. Me and my neighbor were just preparing to do some research on this. We got a grab a book from our area library but I think I learned more from this post. I’m very glad to see such great info being shared freely out there.

  15. Home Page says:

    Thanks a bunch for sharing this with all folks you really understand what you’re speaking about! Bookmarked. Please also talk over with my web site =). We may have a link change arrangement between us!

  16. Well I sincerely liked studying it. This tip offered by you is very helpful for good planning.

  17. Very good written information. It will be valuable to anybody who employess it, as well as yours truly :). Keep up the good work – for sure i will check out more posts.

  18. Whats Going down i’m new to this, I stumbled upon this I’ve discovered It positively helpful and it has helped me out loads. I am hoping to give a contribution

  19. Go Here says:

    Fantastic goods from you, man. I’ve understand your stuff previous to and you are just extremely fantastic. I really like what you have acquired here, really like what you are stating and the way in which you say it. You make it entertaining and you still take care of to keep it sensible. I can’t wait to read far more from you. This is really a tremendous site.

  20. you are actually a good webmaster. The website loading velocity is amazing. It seems that you are doing any distinctive trick. In addition, The contents are masterpiece. you’ve done a excellent process on this subject!

  21. Homepage says:

    I think this is one of the most important info for me. And i’m glad reading your article. But should remark on few general things, The website style is great, the articles is really excellent : D. Good job, cheers

  22. I think other site proprietors should take this website as an model, very clean and great user genial style and design, let alone the content. You’re an expert in this topic!

  23. visit says:

    Pretty section of content. I just stumbled upon your weblog and in accession capital to assert that I acquire actually enjoyed account your blog posts. Anyway I’ll be subscribing to your feeds and even I achievement you access consistently quickly.

  24. I truly wanted to post a brief comment to be able to thank you for some of the lovely pointers you are sharing at this site. My incredibly long internet search has at the end of the day been honored with excellent facts and techniques to share with my good friends. I ‘d express that most of us site visitors are really lucky to exist in a wonderful site with so many special people with insightful tips and hints. I feel rather blessed to have come across your site and look forward to so many more entertaining moments reading here. Thanks a lot once more for all the details.

  25. Website says:

    I get pleasure from, result in I found just what I used to be taking a look for. You have ended my four day lengthy hunt! God Bless you man. Have a great day. Bye

  26. You completed a few nice points there. I did a search on the theme and found nearly all persons will consent with your blog.

  27. Going Here says:

    I simply wanted to say thanks again. I’m not certain the things I would’ve carried out in the absence of these points provided by you regarding such field. It was before a real daunting difficulty in my view, nevertheless considering the very skilled style you treated the issue took me to jump with fulfillment. Extremely grateful for the advice and then expect you comprehend what a great job that you’re putting in training the rest all through your site. Most likely you’ve never met all of us.

  28. Click Here says:

    My brother suggested I might like this website. He was entirely right. This post actually made my day. You can not imagine simply how much time I had spent for this info! Thanks!

  29. Click Here says:

    Undeniably believe that which you said. Your favorite justification appeared to be on the net the easiest thing to be aware of. I say to you, I definitely get annoyed while people consider worries that they just don’t know about. You managed to hit the nail upon the top and also defined out the whole thing without having side effect , people could take a signal. Will probably be back to get more. Thanks

  30. I was just looking for this info for a while. After 6 hours of continuous Googleing, finally I got it in your web site. I wonder what’s the lack of Google strategy that do not rank this type of informative websites in top of the list. Generally the top web sites are full of garbage.

  31. read more says:

    Wonderful paintings! This is the kind of info that are meant to be shared around the web. Shame on the search engines for no longer positioning this submit upper! Come on over and discuss with my website . Thank you =)

  32. more info says:

    You are a very clever person!

  33. Click Here says:

    My brother suggested I might like this website. He was entirely right. This post actually made my day. You can not imagine simply how much time I had spent for this info! Thanks!

  34. I love gathering useful information , this post has got me even more info! .

  35. Wow, marvelous blog structure! How lengthy have you ever
    been running a blog for? you make running a blog glance easy.
    The whole glance of your web site is excellent, let alone the content material!

  36. Wow, fantastic weblog format! How long have you ever been running a blog for? you make blogging glance easy. The full glance of your web site is wonderful, let alone the content!!

  37. I used to be able to find good information from your blog posts.

  38. Hmm it appears like your website ate my first comment
    (it was super long) so I guess I’ll just sum it up what I wrote
    and say, I’m thoroughly enjoying your blog. I too am an aspiring blog writer but I’m still new to
    everything. Do you have any recommendations for novice blog writers?
    I’d definitely appreciate it.

  39. I’m gone to say to my little brother, that he should also
    pay a quick visit this blog on regular basis to obtain updated from most up-to-date
    information.

  40. or asmr says:

    excellent post, very informative. I’m wondering
    why the opposite experts of this sector do not realize this.
    You must proceed your writing. I’m sure, you’ve a huge readers’ base already!

Leave a Reply

Your email address will not be published. Required fields are marked *

Enter Captcha Here : *

Reload Image