How to efficiently scale and crop images in an ASP.NET app? - asp.net

We're having problems with an ASP.NET application which allows users to upload, and crop images. The images are all scaled to fixed sizes afterwards. We basically run out of memory when a large file is processed; it seems that the handling of JPEG is rather inefficient -- we're using System.Drawing.BitMap. Do you have any general advice, and perhaps some pointers to a more efficient image handling library? What experiences do you have?

I had the same problem, the solution was to use System.Drawing.Graphics to do the transformations and dispose every bitmap object as soon as I was finished with it. Here's a sample from my library (resizing) :
public Bitmap ApplyTo(Bitmap bitmap)
{
using (bitmap)
{
Bitmap newBitmap = new Bitmap(bitmap, CalculateNewSize(bitmap));
using (Graphics graphics = Graphics.FromImage(newBitmap))
{
graphics.SmoothingMode =
SmoothingMode.None;
graphics.InterpolationMode =
InterpolationMode.HighQualityBicubic;
graphics.CompositingQuality =
CompositingQuality.HighQuality;
graphics.DrawImage(
bitmap,
new Rectangle(0, 0, newBitmap.Width, newBitmap.Height));
}
return newBitmap;
}
}

I found imageresizer and its great. and good API. Works Great.
Downloaded from Visual studio 2010 Extension Manager: http://nuget.org/.
Easy Steps to download API in VS-2010:
1). Install Extension http://nuget.org/.
3). Find and Install ImageResizing
4).Then Code: (I m using here cropping. you can use any) Documentation on imageresizing.net
string uploadFolder = Server.MapPath(Request.ApplicationPath + "images/");
FileUpload1.SaveAs(uploadFolder + FileUpload1.FileName);
//The resizing settings can specify any of 30 commands.. See http://imageresizing.net for details.
ResizeSettings resizeCropSettings = new ResizeSettings("width=200&height=200&format=jpg&crop=auto");
//Generate a filename (GUIDs are safest).
string fileName = Path.Combine(uploadFolder, System.Guid.NewGuid().ToString());
//Let the image builder add the correct extension based on the output file type (which may differ).
fileName = ImageBuilder.Current.Build(uploadFolder + FileUpload1.FileName, fileName, resizeCropSettings, false, true);
Try!!! its very awsumm and easy to use. thanks.

A couple of thoughts spring to mind -
What size of images do you allow
your users to upload and can you
impose restrictions on this?
When you're using the
System.Drawing.Bitmap class, are you
remembering to dispose of it
correctly? We found one of the primary causes of System.OutOfMemoryException exceptions on our shared hosting platform was users not disposing of Bitmap objects correctly.
Kev

There was an older bug with .net that all images would default to 32 bits per pixel - at this size you can exhaust your memory pretty fast. Please use PixelFormat structure to make sure this is not the case for your problem.
This link might help: http://msdn.microsoft.com/en-us/library/aa479306.aspx

Do you perhaps have a stack trace to look at?
I have also done some image editing after a user has uploaded an image. The only problems that I ran into were restrictions on file upload size on the browsers and timeouts. But nothing related to .Net's libraries.
Something else to consider. If you are doing multiple images or have some dangerous looping somewhere then are you making sure to flush() and dispose() things.

Related

Convert ASP.NET Web Form App with Image Uploads to Azure

So I'm working on moving a web app to Azure and the one thing that is tripping me up is how to deal with image uploads. Currently, I'm handling uploads with ImageResizer and saving them to a directory:
Protected Function DoHeaderUpload() As Guid?
If HeaderUploadControl.HasFile Then
If HeaderUploadControl.PostedFile.ContentType.StartsWith("image/") Then
If HeaderUploadControl.PostedFile.ContentLength < 3145728 Then ' 3MB max size
Dim myFileGuid = System.Guid.NewGuid()
Dim i As ImageResizer.ImageJob = New ImageResizer.ImageJob(HeaderUploadControl.PostedFile,
"~/Images/Headers/" & myFileGuid.ToString() & ".jpg",
New ImageResizer.Instructions("height=100;format=jpg;mode=crop"))
i.Build()
Return myFileGuid
Else
HeaderUploadError.Text = "<br>File exceeds 3MB maximum size"
HeaderUploadError.IsValid = False
End If
Else
HeaderUploadError.Text = "<br>Only image uploads are supported"
HeaderUploadError.IsValid = False
End If
End If
End Function
I found a code excerpt that will populate a blob from the memory stream output from ImageResizer (link).
However, I'm a bit unclear on how to embed/reference these images in my site. Currently I'm just doing this (for example):
HeaderImage.Src = String.Format("/Images/Headers/{0}.jpg", .HeaderImage.ToString())
I guess I could just include the full URL prefix to the blob, but that seems like a bad idea (and would break things when running locally for testing).
Could I use a virtual path that points to an Azure Files share instead of Azure Blob?
I saw AzureReader2 for ImageResizer which is tempting, but it seems super expensive. It seems like that could be a pretty easy solution as well, since it looks like it auto-redirects references with a certain prefix to the Azure Blob location.
I guess the question is: what is the easiest way to do this? I've kind of been going in circles with the various options.
Thanks!
I ended up using the solution where I prefix the appropriate URL to get to the blob storage. To make it work locally and remotely, I added a transform:
<appSettings>
<add key="BlobURLPrefix" value="http://myapp.blob.core.windows.net"
xdt:Locator="Match(key)"
xdt:Transform="SetAttributes(value)"/>
</appSettings>
Then when I need to reference an image location, I can do something like this:
FullUrl = ConfigurationManager.AppSettings("BlobURLPrefix") + ImagePath
Not entirely elegant, but it suits my purposes reasonably well and is pretty easy to implement.

a generic error occurred in gdi+. while saving an image

Dear Expert i am getting an error while saving an image the code is as follows
ClsImageManager objImgManager = new ClsImageManager();
Bitmap ImageBitmap = objImgManager.GetBitmapFromBytes(ImageData);
Response.ContentType = "image/tiff";
ImageBitmap.Save(Response.OutputStream, ImageFormat.Tiff);
ImageBitmap.Dispose();
Response.End();
when i used Image.format.jpeg the code is working good but when i changes it to ImageFormat.Tiff then i am getting an error a generic error occurred in gdi+.
You should note that GDI/GDI+ (System.Drawing namespace) is not officially supported in ASP.NET - see "Caution" in http://msdn.microsoft.com/en-us/library/system.drawing.aspx.
WIC is supposed to be used instead GDI+ (see http://weblogs.asp.net/bleroy/archive/2009/12/10/resizing-images-from-the-server-using-wpf-wic-instead-of-gdi.aspx)
Said that, many had successfully use GDI+ in ASP.NET. Most probably, you should attempt saving image into memory stream (or on file) and then writing saved image into the response. See this link for details: http://www.west-wind.com/weblog/posts/2006/Oct/19/Common-Problems-with-rendering-Bitmaps-into-ASPNET-OutputStream
Another work-around can be related to user account. Apparently, GDI/GDI+ is bound to device context (screen, printer etc) and they may not be available under service accounts. So you may try running your ASP.NET code on some normal user account if that helps or not.
You may need to try explicitly encoding the image save.
Have a look at the code example at the bottom of this MSDN documentation on Image.Save
Image.Save Method (String, ImageCodecInfo, EncoderParameters)
The same actions can be applied to your save.
However, it could also possibly be that your objImgManager is disposing of the buffer where the image is stored before you can save it.
Bitmap ImageBitmap = objImgManager.GetBitmapFromBytes(ImageData);
You can get around this by creating a copy of the image by doing this:
Bitmap ImageBitmap = new Bitmap(objImgManager.GetBitmapFromBytes(ImageData));

Loader for functions?

I am attempting to monitor the progress of an image scroller I've built and all of the images (thumbs) load separately. What would be the best way of figuring out what the total progress of the images that are loading?
I was thinking it would be cool to use a generic loader and apply it to a function such as
myScroller.loadImages();
But I haven't seen any examples of that.
I did come across this thread:
but I'm not sure if it really addresses the issue I'm up against or not.
Thanks for any thoughts or expertise.
jml
Hey there, I would lean towards the 3rd. option mentioned by maxmc. I'd have an XML with the info. of all the images I want to load (e.g. name, size, etc); this way I will know ahead how many images are going to be loaded, if I want to measure the overall progess based on the number of the remaining images, but also the total bytes I have to load in case I want to monitor the overall progress in terms of remaining bytes to load.
This XML of course could be created on demand; you just need to create a script that gets, from flash, the folder or location which you want to load images from, so it reads it, get the necessary info about the images and returns to flash the result as XML so it can proceed with the load images process.
the problem is that you only know the size of an image if you start loading it so there are three options i guess:
1) start loading each of the image and stop immediately after you get the size. the disadvantage is that you double the requests.
2) when you start to load the first image, multiply it's size with the number of images. disadvantage is that this is inprecise.
3) if you define the images somewhere outside your app for instance in an xml you could add some kind of filesize attribute and when you compile your app inject the sizes via some custom ant task into the xml.
i favour option 3.
Depending on the scale of your app you could look into the bulkloader library. It's not exactly lightweight, about 16kb I think, but if you're loading quite a few images then that shouldn't be too much of an impact. Check the site for detailed instructions but you do something like this:
private var $loader : Bulkloader = new BulkLoader( "ExampleLoader" );
private function startLoad() : void
{
$loader.add( "image1.jpg", { id : "image1", type : "image" });
$loader.add( "image2.jpg", { id : "image2", type : "image" });
$loader.add( "image3.jpg", { id : "image3", type : "image" });
$loader.addEventListener( BulkLoaderEvent.COMPLETE, loadComplete )
$loader.start();
}
private function loadComplete( e : BulkLoader ) : void
{
var thumbNail : ThumbNail = new ThumbNail( $loader.getBitmap( "image1" ) );
}
Not sure if that's exactly right but you should get the idea.
Rich

Preventing Flex application caching in browser (multiple modules)

I have a Flex application with multiple modules.
When I redeploy the application I was finding that modules (which are deployed as separate swf files) were being cached in the browser and the new versions weren't being loaded.
So i tried the age old trick of adding ?version=xxx to all the modules when they are loaded. The value xxx is a global parameter which is actually stored in the host html page:
var moduleSection:ModuleLoaderSection;
moduleSection = new ModuleLoaderSection();
moduleSection.visible = false;
moduleSection.moduleName = moduleName + "?version=" + MySite.masterVersion;
In addition I needed to add ?version=xxx to the main .swf that was being loaded. Since this is done by HTML I had to do this by modifying my AC_OETags.js file as below :
function AC_FL_RunContent(){
var ret =
AC_GetArgs
( arguments, ".swf?mv=" + getMasterVersion(), "movie", "clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
, "application/x-shockwave-flash"
);
AC_Generateobj(ret.objAttrs, ret.params, ret.embedAttrs);
}
This is all fine and works great. I just have a hard time believing that Adobe doesn't already have a way to handle this. Given that Flex is being targeted to design modular applications for business I find it especially surprising.
What do other people do? I need to make sure my application reloads correctly even if someone has once per session selected for their 'browser cache checking policy'.
I had a similar problem, and ended up putting the SWF files in a sub-directory named as the build number. This meant that the URL to the SWF files pointed to a different location each time.
Ideally this should be catered for by the platform, but no joy there. But this works perfectly for us, and integrates very easily into our automated builds with Hudson - no complaints so far.
Flex says:
http://www.adobe.com/livedocs/flex/2/docs/wwhelp/wwhimpl/common/html/wwhelp.htm?context=LiveDocs_Parts&file=00001388.html
What I have done is checksum the SWF file and then add that to its url. Stays the same until the file is rebuilt/redeployed. Handled automagically by a few lines of server-side PHP script
here is sample.
function AC_FL_RunContent(){
var ret = AC_GetArgs(arguments, ".swf?ts=" + getTS(), "movie",
"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000",
"application/x-shockwave-flash");
AC_Generateobj(ret.objAttrs, ret.params, ret.embedAttrs);
}
function getTS() {
var ts = new Date().getTime();
return ts;
}
AC_OETags.js is file and it exists html-template several places.
but as my posting said, I am facing another type of problem.
The caching is not done by Flash Player but by the browser, so it's out of Adobe's control. I think you have found a workable solution. If I want to avoid caching I usually append a random number on the URL.

Printing in Adobe AIR - Standalone PDF Generation

Is it possible to generate PDF Documents in an Adobe AIR application without resorting to a round trip web service for generating the PDF? I've looked at the initial Flex Reports on GoogleCode but it requires a round trip for generating the actual PDF.
Given that AIR is supposed to be the Desktop end for RIAs is there a way to accomplish this? I suspect I am overlooking something but my searches through the documentation don't reveal too much and given the target for AIR I can't believe that it's just something they didn't include.
There's AlivePDF, which is a PDF generation library for ActionScript that should work, it was made just for the situation you describe.
Just added a Adobe Air + Javascript + AlivePDF demo:
This demo doesn't require flex and is pretty straight forward.
http://www.drybydesign.com/2010/02/26/adobe-air-alivepdf-without-flex/
One of the other teams where I work is working on a Flex-based drawing application and they were totally surprised that AIR / Flex does not have PDF authoring built-in. They ended up rolling their own simple PDF creator based on the PDF specification.
Yes it is very easy to create PDF using AlivePDF, here is the sample code, first method create a pdf and second method save the pdf on disk and return the path, feel free to ask any question.
public function createFlexPdf() : String
{
pdf = new PDF();
pdf.setDisplayMode (Display.FULL_WIDTH,Layout.ONE_COLUMN,Mode.FIT_TO_PAGE,0.96);
pdf.setViewerPreferences(ToolBar.SHOW,MenuBar.HIDE,WindowUI.SHOW,FitWindow.RESIZED,CenterWindow.CENTERED);
pdf.addPage();
var myFontStyle:IFont = new CoreFont ( FontFamily.COURIER );
pdf.setFont(myFontStyle,10);
pdf.addText('Kamran Aslam',10,20);//String, X-Coord, Y-Coord
return savePDF();
}
private function savePDF():String
{
var fileStream:FileStream = new FileStream();
var file:File = File.createTempDirectory();
file = file.resolvePath("temp.pdf");
fileStream.open(file, FileMode.WRITE);
var bytes:ByteArray = pdf.save(Method.LOCAL);
fileStream.writeBytes(bytes);
fileStream.close();
return file.url;
}

Resources