We've been getting this issue for a long time now, and tried a lot of different fixes from the net. So far nothing worked.
Issue: The Base Image is saving just fine, thumbnails are failing on the save. Before the solution below, I have tried creating separate streams for all images (base image, 600x600 and 300x300 thumb) and this didn't work either. All of the streams were constructed from the same byte array. Keep this in mind: This works just fine on Development environment, Test environment and Test2 environment however fails to work on Production environment. I have checked all the settings / environment variables / permissions on folders and everything is setup the same as the Test environments.
Paths are coming in as follows:
- Base Path: "~/images/imageUpl/"
- Thumbnail Add. Path: "thumbM/"
- Thumbnail Add. Path: "thumbS/"
- Image name struct: "X_YYYYMMDD_HHMMSS_XX.png"
The paths are all correct - as it works Dev/Test/Test2 environments.
Any help on this is much appreciated!
EDIT: What we tried so far:
- Set permissions for Network and IISUser
- Use separate streams for each image constructed from original source data
- Adding thread.sleep(30+) as per other examples
- Creating a fresh Bitmap from the resized one and saving that
- Different path to test if its directory issue on production
EDIT 2: For reference, this is a ASP.NET MVC5 Web Application, running of .NET Framework 4.7.2.
Image Processor Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Drawing.Drawing2D;
using Newtonsoft.Json;
using System.Threading;
namespace CMS_MVC.Classes
{
public class ImageProcessor : IDisposable
{
public enum PathType : int
{
Relative = 1,
Static = 2
}
private Stream _ImageStream { get; set; }
private HttpContext _Context { get; set; }
public Image BaseImage { get; set; }
private int _instanceId { get; set; }
public ImageProcessor(int instanceId, Stream imageStream, HttpContext context)
{
this._ImageStream = imageStream;
this._Context = context;
this._instanceId = instanceId;
this.BaseImage = Image.FromStream(this._ImageStream);
}
public ImageProcessor(int instanceId, byte[] imageByteArray, HttpContext context)
{
this._Context = context;
this._instanceId = instanceId;
this._ImageStream = new MemoryStream(imageByteArray);
this.BaseImage = Image.FromStream(this._ImageStream);
}
public ImageProcessor(int instanceId, string imagePath, PathType pathType, HttpContext context)
{
this._Context = context;
this._instanceId = instanceId;
if (pathType == PathType.Relative)
{
this._ImageStream = new MemoryStream(File.ReadAllBytes(this._Context.Server.MapPath(imagePath)));
this.BaseImage = Image.FromStream(this._ImageStream);
}
else
{
this._ImageStream = new MemoryStream(File.ReadAllBytes(imagePath));
this.BaseImage = Image.FromStream(this._ImageStream);
}
}
public Dictionary<string, bool> SaveImages(string baseImageSavePath, string imageName, Dictionary<string, Tuple<int, int>> thumbnails = null)
{
Dictionary<string, bool> results = new Dictionary<string, bool>();
string lastResult = "main";
results.Add(lastResult, true);
try
{
this.BaseImage.Save(this._Context.Server.MapPath(Path.Combine(baseImageSavePath, imageName)), ImageFormat.Png);
if (thumbnails != null)
{
foreach (var thumbnail in thumbnails)
{
lastResult = thumbnail.Value.Item1.ToString() + "_" + thumbnail.Value.Item2.ToString();
results.Add(lastResult, true);
using (Bitmap thumbBitmap = this.ResizeImage(thumbnail.Value.Item1, thumbnail.Value.Item2))
{
Thread.Sleep(50);
thumbBitmap.Save(this._Context.Server.MapPath(Path.Combine(baseImageSavePath + thumbnail.Key, imageName)), ImageFormat.Png);
Thread.Sleep(50);
}
}
}
}
catch (Exception ex)
{
results[lastResult] = false;
// Log event
}
return results;
}
private Bitmap ResizeImage(int targetWidth, int targetHeight)
{
Tuple<int, int> destSize = this.CalculateThumbnailSizeAspectRatio(targetWidth, targetHeight);
var destRect = new Rectangle(0, 0, destSize.Item1, destSize.Item2);
var destImage = new Bitmap(destSize.Item1, destSize.Item2);
destImage.SetResolution(this.BaseImage.HorizontalResolution, this.BaseImage.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(this.BaseImage, destRect, 0, 0, this.BaseImage.Width, this.BaseImage.Height, GraphicsUnit.Pixel, wrapMode);
}
}
return destImage;
}
private Tuple<int, int> CalculateThumbnailSizeAspectRatio(int targetWidth, int targetHeight)
{
// Resize calculations
}
public void Dispose()
{
if (this._ImageStream != null) this._ImageStream.Dispose();
if (this.BaseImage != null) this.BaseImage.Dispose();
}
}
}