0

Consider the 3 versions of file creation below, for various reasons I have different methods to add files sent from a Windows Forms application (using HttpClient) to an ASP.NET Core 2.2 web application.

Quite simply I create a file on the file system and then immediately create a thumbnail image if the file is an image type file (extensions are in the files array).

When I run this locally all is fine, images and thumbnails (where applicable) get created. However, when I run this deployed I get no thumbnails. ScaleImage won't create a thumbnail if it can find a file and will return an empty string.

It's a timing thing I think but has anyone come across this before?

            //v1
            await File.WriteAllBytesAsync(filePath, Convert.FromBase64String(result.Data));
            string thumbnail = files.Contains(sFileExt) ? SystemFunctions.ScaleImage(filePath, 192, 192) : "";

            //v3
            await file.CopyToAsync(stream);
            string thumbnail = files.Contains(sFileExt) ? SystemFunctions.ScaleImage(filePath, 192, 192) : "";

            //v3
            await writer.WriteAsync(result.Data);
            writer.Close();
            string thumbnail = files.Contains(sFileExt) ? SystemFunctions.ScaleImage(filePath, 192, 192) : "";

ScaleImage function

    //create a thumbnail file from the supplied image respecting the aspect ratio
    //returns the path the the thumbnail or empty string if the thumbnail can't be
    //created for some reason
    internal static string ScaleImage(string fileName, int maxWidth, int maxHeight)
    {
        if (File.Exists(fileName))
        {
            try
            {
                Image oImage = Image.FromFile(fileName);

                double dRatioX = (double)maxWidth / oImage.Width;
                double dRatioY = (double)maxHeight / oImage.Height;
                double dRatio = Math.Min(dRatioX, dRatioY);

                int iNewWidth = (int)(oImage.Width * dRatio);
                int iNewHeight = (int)(oImage.Height * dRatio);

                Image oNewImage = new Bitmap(iNewWidth, iNewHeight);

                Graphics.FromImage(oNewImage).DrawImage(oImage, 0, 0, iNewWidth, iNewHeight);

                string filePath = Path.GetDirectoryName(fileName);
                string fileExt = Path.GetExtension(fileName);
                string newFileName = Guid.NewGuid().ToString();

                newFileName = Path.Combine(filePath, $"{newFileName}{fileExt}");

                oNewImage.Save(newFileName);

                WriteToErrorLog(newFileName);

                return newFileName;
            }
            catch (Exception e)
            { 
                var stackFrame = new System.Diagnostics.StackTrace(e, true);                    
                var frame = stackFrame.GetFrame(0);                    
                var line = frame.GetFileLineNumber();
                WriteToErrorLog($"Error: {e.Message}({line}) [{frame.GetMethod().Name}]: {fileName}\r\n{e}");
                return ""; 
            }
        }

        WriteToErrorLog($"File Not Found: {fileName}");
        return "";
    }
djack109
  • 1,261
  • 1
  • 23
  • 42
  • It doesn't appear that you're disposing of your `IDisposable` references. When working with graphics this can quickly cause your program to fail. – Enigmativity Apr 27 '20 at 09:45
  • And never ever ever write `catch (Exception e)`. It's just an invitation for buggy code. Only ever capture specific exceptions that you can meaningfully handle. – Enigmativity Apr 27 '20 at 09:46

2 Answers2

0

Not directly the answer, but start by checking if any exceptions occur. Don't swallow exceptions, log them and react on them....

    internal static string ScaleImage(string fileName, int maxWidth, int maxHeight)
    {
        if (File.Exists(fileName))
        {
            try
            {
                using( var oImage = Image.FromFile(fileName)) 
                {
                  double dRatioX = (double)maxWidth / oImage.Width;
                  double dRatioY = (double)maxHeight / oImage.Height;
                  double dRatio = Math.Min(dRatioX, dRatioY);

                  int iNewWidth = (int)(oImage.Width * dRatio);
                  int iNewHeight = (int)(oImage.Height * dRatio);

                  using( var oNewImage = new Bitmap(iNewWidth, iNewHeight))
                  using( var gfx = Graphics.FromImage(oNewImage))
                  {
                    gfx.DrawImage(oImage, 0, 0, iNewWidth, iNewHeight);

                    string filePath = Path.GetDirectoryName(fileName);
                    string fileExt = Path.GetExtension(fileName);
                    string newFileName = Guid.NewGuid().ToString();

                    newFileName = Path.Combine(filePath, $"{newFileName}{fileExt}");

                    oNewImage.Save(newFileName);
                    return newFileName;
                 }
               }
            }
            catch(Exception ex)
           { 
             // do some logging here.(Log to file or MessageBox.Show()...)
             return "";
           }
        }

        return "";
    }
Frank Nielsen
  • 1,546
  • 1
  • 10
  • 17
  • Can't add a message box as its an ASP.NET Core web application. I added a log file (I should have thought of that) but no errors logged. Strangely it doesn't log anything at all, I set it up to log a message pass or fail. (I updated my question) – djack109 Apr 27 '20 at 09:35
  • Sorry, i mixed my mindset with another winforms question. But if no errors appears in the log, the issue must be somewhere else. Are you sure about the file extensions and their case sensitivity. Maybe always let it create the thumbnail - just for testing. – Frank Nielsen Apr 27 '20 at 09:47
  • Manage to track it down to an `Out of memory.` error on `Image.FromFile`. I suspect its a timing issue because at the point I make the call to `ScaleImage` to create the thumbnail the file has not completed creation or isn't in a state that `ScaleImage` can process it. Given that all my file creation calls are `async`. I'll have to do more digging – djack109 Apr 27 '20 at 11:11
  • no it is not an timing issue. You are not releasing resources after use, remember always to have `using` block where required. Updated the answer. – Frank Nielsen Apr 27 '20 at 11:16
  • Nope, that didn't fix it. My debugging tells me the problem is here `oImage = Image.FromFile(fileName)` with and without the using statements. I updated my original question with some enhanced error logging. `System.OutOfMemoryException: Out of memory. at System.Drawing.Image.FromFile(String filename, Boolean useEmbeddedColorManagement)` – djack109 Apr 27 '20 at 11:30
  • neilsen Seems like to were correct about not releasing reasources. `using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))` fixed the problem – djack109 Apr 27 '20 at 11:50
0

Here is what finally solved the problem. Thanks to this post

    internal static string ScaleImage(string fileName, int maxWidth, int maxHeight)
    {
        if (File.Exists(fileName))
        {
            try
            {
                using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                {
                    using (var oImage = Image.FromStream(fs))
                    {
                        double dRatioX = (double)maxWidth / oImage.Width;
                        double dRatioY = (double)maxHeight / oImage.Height;
                        double dRatio = Math.Min(dRatioX, dRatioY);

                        int iNewWidth = (int)(oImage.Width * dRatio);
                        int iNewHeight = (int)(oImage.Height * dRatio);

                        using (var oNewImage = new Bitmap(iNewWidth, iNewHeight))
                        {
                            Graphics.FromImage(oNewImage).DrawImage(oImage, 0, 0, iNewWidth, iNewHeight);

                            string filePath = Path.GetDirectoryName(fileName);
                            string fileExt = Path.GetExtension(fileName);
                            string newFileName = Guid.NewGuid().ToString();

                            newFileName = Path.Combine(filePath, $"{newFileName}{fileExt}");

                            oNewImage.Save(newFileName);

                            oImage.Dispose();
                            oNewImage.Dispose();

                            WriteToErrorLog($"Successfully created thumbnail: {newFileName}");

                            return newFileName;
                        }
                    }
                }
            }
            catch (Exception e)
            {
                var stackFrame = new System.Diagnostics.StackTrace(e, true);                    
                var frame = stackFrame.GetFrame(0);                    
                var line = frame.GetFileLineNumber();
                WriteToErrorLog($"Error: {e.Message}({line}) [{frame.GetMethod().Name}]: {fileName}\r\n{e}");

                return "";
            }
        }

        WriteToErrorLog($"File Not Found: {fileName}");
        return "";
    }
djack109
  • 1,261
  • 1
  • 23
  • 42