I was just looking at this bug fix (http://www.codeplex.com/dashCommerce/WorkItem/View.aspx?WorkItemId=15303), and thought I would share how I addressed the problem. (I posted a comment there as well, but thought I would post it here too, since I read the forums more often). I ended up making a few changes that save 3 versions of every product image as soon as it is uploaded (normal size, thumbnail, and a tiny one). Please note: This ONLY applies to product image uploads.
This may be a bit overkill - but personally, if I'm going to display an image at a number of different sizes - I like to save them at the size that they will be displayed. This way, if I have a list of 20 product images, I'm not forcing the user to download images that are potentially 500K EACH!!! The way I handled this was to save multiple versions of an image as soon as it's uploaded. So, when you're adding an image to a product, and you upload it - I wrote some code to save the original image, as well as 2 additional versions that are resized to my specifications. So, I'll try to explain more below...
First, add these settings to your web.config file. You can change the dimensions, this is just what my designer called for...
<appSettings>
<add key="maxWidth" value="545" />
<add key="maxHeight" value="420" />
<add key="maxThumbWidth" value="125" />
<add key="maxThumbHeight" value="125" />
<add key="maxTinyWidth" value="85" />
<add key="maxTinyHeight" value="65" />
</appSettings>
Next, under the "repository/product" folder, add two folders: "thumb" and "tiny" - and make sure they have write permissions on them, just like the "repository/product" folder needs.
Now, to the code. Open up "Web/admin/imageselector.aspx.cs". Within the "Constants" region, add these two lines:
private const string PRODUCT_THUMB_IMAGE_PATH = @"~/repository/product/thumb/";
private const string PRODUCT_TINY_IMAGE_PATH = @"~/repository/product/tiny/";
Next, within the "Member Variables" region, add these 2 lines:
private string pathToThumb = string.Empty;
private string pathToTiny = string.Empty;
Next, within the "Page Events" region, you're going to modify the "p" case in the "switch" block like so:
case "p":
pnlImages.GroupingText = LocalizationUtility.GetText("lblProductImages");
path = PRODUCT_IMAGE_PATH;
pathToThumb = PRODUCT_THUMB_IMAGE_PATH;
pathToTiny = PRODUCT_TINY_IMAGE_PATH;
break;
Now, to the actual code that saves the images. Replace the entire contents of the "btnUpload_Click" method with this:
try {
HttpPostedFile file = fuFile.PostedFile;
if(file.ContentLength > 0) {
int maxHeight = 0;
int maxWidth = 0;
maxHeight = Int32.Parse(System.Web.Configuration.WebConfigurationManager.AppSettings["maxHeight"]);
maxWidth = Int32.Parse(System.Web.Configuration.WebConfigurationManager.AppSettings["maxWidth"]);
//FileWriter fileWriter = new FileWriter();
string finalPath = HttpContext.Current.Server.MapPath(path) + fuFile.FileName;
//fileWriter.Write(finalPath, file.InputStream);
ResizeAndSave(fuFile, finalPath, maxHeight, maxWidth);
if (!string.IsNullOrEmpty(pathToThumb))
{
//now resize to thumb and save
maxHeight = Int32.Parse(System.Web.Configuration.WebConfigurationManager.AppSettings["maxThumbHeight"]);
maxWidth = Int32.Parse(System.Web.Configuration.WebConfigurationManager.AppSettings["maxThumbWidth"]);
finalPath = HttpContext.Current.Server.MapPath(pathToThumb) + fuFile.FileName;
ResizeAndSave(fuFile, finalPath, maxHeight, maxWidth);
}
if (!string.IsNullOrEmpty(pathToTiny))
{
//now resize to tiny and save
maxHeight = Int32.Parse(System.Web.Configuration.WebConfigurationManager.AppSettings["maxTinyHeight"]);
maxWidth = Int32.Parse(System.Web.Configuration.WebConfigurationManager.AppSettings["maxTinyWidth"]);
finalPath = HttpContext.Current.Server.MapPath(pathToTiny) + fuFile.FileName;
ResizeAndSave(fuFile, finalPath, maxHeight, maxWidth);
}
LoadImageList();
Master.MessageCenter.DisplaySuccessMessage(LocalizationUtility.GetText("lblImageSaved"));
}
}
catch(Exception ex) {
Logger.Error(typeof(imageselector).Name + ".btnUpload_Click", ex);
Master.MessageCenter.DisplayCriticalMessage(LocalizationUtility.GetCriticalMessageText(ex.Message));
}
Next, add this new method, which does the resizing (if the image is larger than your maxHeight or maxWidth) and saving:
private void ResizeAndSave(FileUpload file, string finalPath, int maxHeight, int maxWidth)
{
// Create a bitmap in memory of the content of the fileUpload control
System.Drawing.Bitmap originalBMP = new System.Drawing.Bitmap(file.FileContent);
// Calculate the new image dimensions
int width = originalBMP.Width; //actual width
int height = originalBMP.Height; //actual height
int widthDiff = (width - maxWidth); //how far off maxWidth?
int heightDiff = (height - maxHeight); //how far off maxHeight?
//figure out which dimension is further outside the max size
bool doWidthResize = (maxWidth > 0 && width > maxWidth && widthDiff > -1 && widthDiff > heightDiff);
bool doHeightResize = (maxHeight > 0 && height > maxHeight && heightDiff > -1 && heightDiff > widthDiff);
//only resize if the image is bigger than the max
if (doWidthResize || doHeightResize)
{
int iStart;
Decimal divider;
if (doWidthResize)
{
iStart = width;
divider = Math.Abs((Decimal)iStart / (Decimal)maxWidth);
width = maxWidth;
height = (int)Math.Round((Decimal)(height / divider));
}
else
{
iStart = height;
divider = Math.Abs((Decimal)iStart / (Decimal)maxHeight);
height = maxHeight;
width = (int)Math.Round((Decimal)(width / divider));
}
}
// Create a new bitmap which will hold the previous resized bitmap
System.Drawing.Bitmap newBMP = new System.Drawing.Bitmap(originalBMP, width, height);
// Create a graphic based on the new bitmap
System.Drawing.Graphics oGraphics = System.Drawing.Graphics.FromImage(newBMP);
// Set the properties for the new graphic file
oGraphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
oGraphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
// Draw the new graphic based on the resized bitmap
oGraphics.DrawImage(originalBMP, 0, 0, width, height);
// Save the new graphic file to the server
newBMP.Save(finalPath);
// Once finished with the bitmap objects, we deallocate them.
originalBMP = null;
newBMP = null;
oGraphics = null;
}
FINALLY, you'll want to change your code to actually USE these optimized images when appropriate. First, in "Web/Controls/catalogList.ascx.cs", find where the "thumb.ImageUrl" is set, and modify the url:
System.Web.UI.WebControls.Image thumb = e.Item.FindControl("imgThumb") as System.Web.UI.WebControls.Image;
if (thumb != null && !string.IsNullOrEmpty(product.DefaultImagePath) && File.Exists(Server.MapPath(product.DefaultImagePath)))
thumb.ImageUrl = product.DefaultImagePath.Replace("product/", "product/thumb/");
Then, in the "Web/product.aspx.cs" file, replace the entire "LoadProductImages" method with this version:
/// <summary>
/// Loads the product images.
/// </summary>
private void LoadProductImages () {
ImageCollection imageCollection = _product.ImageRecords();
imageCollection.Sort("SortOrder", true);
Img.Image drawnImage;
System.Web.UI.WebControls.Image displayImage;
foreach (Store.Image image in imageCollection) {
if (File.Exists(Server.MapPath(image.ImageFile))) {
string origPath = image.ImageFile;
string tinyPath = origPath.Replace("product/", "product/tiny/");
drawnImage = Img.Image.FromFile(Server.MapPath(tinyPath));
displayImage = new System.Web.UI.WebControls.Image();
displayImage.ImageUrl = tinyPath;
displayImage.Attributes.Add("onmouseover", string.Format("{0}.src='{1}';document.getElementById('{2}').innerHTML='{3}';", defaultImage.ClientID, Page.ResolveUrl(origPath), imageCaption.ClientID, image.Caption));
displayImage.Width = drawnImage.Width;
displayImage.Height = drawnImage.Height;
imageList.Add(displayImage);
}
}
imageList.TrimExcess();
dlImages.DataSource = imageList;
dlImages.DataBind();
if (File.Exists(Server.MapPath(imageCollection[0].ImageFile))) {
defaultImage.ImageUrl = imageCollection[0].ImageFile;
drawnImage = Img.Image.FromFile(Server.MapPath(imageCollection[0].ImageFile));
defaultImage.Width = drawnImage.Width;
defaultImage.Height = drawnImage.Height;
}
imageCaption.Text = imageCollection[0].Caption;
}
One last thing... I promise!!! (I told you this might be a bit overkill...). Find the "site.skin" file in the App_Themes folder that you are using, and change this:
<asp:Image runat="server" ImageUrl="images/noDefaultImage.gif" height="450px" SkinId="noImage"/>
to this:
<asp:Image runat="server" ImageUrl="images/noDefaultImage.gif" SkinId="noImage"/>
The "height" overrides ANYTHING you try to set from the code.
Whew!!! That's it! I hope that helps someone out there...