GDI读取后上传文件锁

时间:2021-11-25 09:39:53

I have the following ImageObject class:

我有以下ImageObject类:

public class ImageObject
{
    public static Image CropImage(Image img, Rectangle cropArea)
    {
        Bitmap bmpImage = new Bitmap(img);
        Bitmap target = new Bitmap(cropArea.Width, cropArea.Height);
        using(Graphics g = Graphics.FromImage(target))
        {
            g.DrawImage(bmpImage, new Rectangle(0, 0, target.Width, target.Height), cropArea, GraphicsUnit.Pixel);
            g.Dispose();
        }
        return (Image)target;
    }

    public static Image ResizeImage(Image imgToResize, Size size)
    {
        int sourceWidth = imgToResize.Width;
        int sourceHeight = imgToResize.Height;

        float nPercent = 0;
        float nPercentW = 0;
        float nPercentH = 0;

        nPercentW = ((float)size.Width / (float)sourceWidth);
        nPercentH = ((float)size.Height / (float)sourceHeight);

        if (nPercentH < nPercentW)
            nPercent = nPercentH;
        else
            nPercent = nPercentW;

        int destWidth = (int)(sourceWidth * nPercent);
        int destHeight = (int)(sourceHeight * nPercent);

        Bitmap b = new Bitmap(destWidth, destHeight);
        //Graphics g = Graphics.FromImage((Image)b);
        using(Graphics g = Graphics.FromImage((Image)b))
        {
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            g.DrawImage(imgToResize, 0, 0, destWidth, destHeight);
            g.Dispose();    
        }
        return (Image)b;
    }

    public static void SaveJpeg(string path, System.Drawing.Image source, long quality)
    {
        EncoderParameter qualityParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
        ImageCodecInfo jpegCodec = GetEncoderInfo("image/jpeg");
        if (jpegCodec == null)
            return;
        EncoderParameters encoderParams = new EncoderParameters(1);
        encoderParams.Param[0] = qualityParam;
        source.Save(path, jpegCodec, encoderParams);
    }

    private static ImageCodecInfo GetEncoderInfo(string mimeType)
    {
        // Get image codecs for all image formats
        ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();

        // Find the correct image codec
        for (int i = 0; i < codecs.Length; i++)
            if (codecs[i].MimeType == mimeType)
                return codecs[i];
        return null;
    }
}

And I reference this code from a function in another class:

我引用另一个类中的函数的代码:

public static void CreateAvatar(string filepath, int x, int y, int w, int h)
{
    var big = filepath + "100x100.jpg";
    var medium = filepath + "40x40.jpg";
    var small = filepath + "25x25.jpg";
    var full_path = filepath + "avatar.jpg";
    var temp_path = filepath + "avatar_t.jpg";

    if (File.Exists(big))
    {
        File.Delete(big);
    }
    if (File.Exists(medium))
    {
        File.Delete(medium);
    }
    if (File.Exists(small))
    {
        File.Delete(small);
    }
    if (File.Exists(temp_path))
    {
        File.Delete(temp_path);
    }

    System.Drawing.Image img = System.Drawing.Image.FromFile(full_path);
    System.Drawing.Rectangle rect = new Rectangle(x, y, w, h);
    System.Drawing.Size hundred = new Size(100, 100);
    System.Drawing.Size forty = new Size(40, 40);
    System.Drawing.Size twentyfive = new Size(25, 25);

    //we crop, then we resize...
    var cropped = ImageObject.CropImage(img, rect);
    ImageObject.SaveJpeg(temp_path, cropped, 100L);

    //problems usually from here. can't save big, because it can't read temp_path - it's locked...
    var resize_big = ImageObject.ResizeImage(System.Drawing.Image.FromFile(temp_path), hundred);
    ImageObject.SaveJpeg(big, resize_big, 100L);

    var resize_forty = ImageObject.ResizeImage(System.Drawing.Image.FromFile(temp_path), forty);
    ImageObject.SaveJpeg(medium, resize_forty, 100L);

    var resize_twentyfive = ImageObject.ResizeImage(System.Drawing.Image.FromFile(temp_path), twentyfive);
    ImageObject.SaveJpeg(small, resize_twentyfive, 100L);
}

This method is called by a web service. On the first execution of this code (after an IIS restart), all is well, but if used again it hangs. I know it has to do with the two images I have created: avatar.jpg and avatar_t.jpg. I know this because I cannot delete or rename the images in Explorer:

此方法由web服务调用。在此代码的第一次执行(在IIS重新启动之后),一切正常,但如果再次使用,则挂起。我知道这与我创建的两个图像有关:avatar.jpg和avatar_t.jpg。我知道这一点是因为我不能删除或重命名资源管理器中的图像:

GDI读取后上传文件锁

I have ensured I have Dispose'd the Graphics objects as suggested by many, but I can't figure out why the locks won't release? Can anyone see the problem?

我已经确定我已经处理了许多图形对象,但是我不知道为什么这些锁不能释放?有人能看到问题吗?

Ideally, I'd like to do this at the bottom:

理想情况下,我想在下面做这个:

var resize_twentyfive = ImageObject.ResizeImage(System.Drawing.Image.FromFile(temp_path), twentyfive);
            ImageObject.SaveJpeg(small, resize_twentyfive, 100L);

//clean up, delete avatar.jpg and avatar_t.jpg
File.Delete(temp_path);
File.Delete(full_path);

And delete the images I used to read from - they are no longer needed. I don't mind them staying there, so long as I can then overwrite them at will from the uploader...

删除我以前读过的图片——它们不再需要了。我不介意他们呆在那里,只要我可以随意地从上传者那里覆盖他们……

1 个解决方案

#1


6  

System.Drawing.Image.FromFile() does not close the file until you call Dispose on the image.

from文件()不关闭文件,直到您调用处理图像。

Bitmap and Image constructor dependencies:

位图和图像构造函数依赖项:

When either a Bitmap object or an Image object is constructed from a file, the file remains locked for the lifetime of the object. As a result, you cannot change an image and save it back to the same file where it originated.

当位图对象或图像对象从文件构造时,该文件将在对象的生命周期内保持锁定。因此,您无法更改映像并将其保存到它的源文件。

Additionally, if the stream was destroyed during the life of the Bitmap object, you cannot successfully access an image that was based on a stream. For example, the Graphics.DrawImage() function may not succeed after the stream has been destroyed

此外,如果流在位图对象的生命周期中被破坏,则不能成功访问基于流的映像。例如,Graphics.DrawImage()函数在流被破坏后可能不会成功

Image.FromFile() is a very poor API method (in the sense that it sets the developer up for failure!). The problem is caused by:

fromfile()是一个非常糟糕的API方法(因为它设置了开发人员失败!)问题的原因如下:

GDI+, and therefore the System.Drawing namespace, may defer the decoding of raw image bits until the bits are required by the image. Additionally, even after the image has been decoded, GDI+ may determine that it is more efficient to discard the memory for a large Bitmap and to re-decode later. Therefore, GDI+ must have access to the source bits for the image for the life of the Bitmap or the Image object.

GDI+,因此系统。绘制命名空间,可以将原始图像位的解码延迟到图像需要这些位。此外,即使在图像被解码后,GDI+可能会决定放弃一个大位图的内存并在以后重新解码,这更有效。因此,GDI+必须在位图或图像对象的生命周期中访问图像的源位。

To retain access to the source bits, GDI+ locks any source file, and forces the application to maintain the life of any source stream, for the life of the Bitmap or the Image object.

为了保持对源比特的访问,GDI+锁住任何源文件,并迫使应用程序为位图或图像对象的生命周期维护任何源流的生命周期。

Again, quoting from the support article:

再一次,引用支助条款:

To work around this problem, create new Bitmap images by using one of the following methods (as described later in this section):

要解决这个问题,可以使用以下方法之一创建新的位图图像(如本节后面所述):

  • Create a non-indexed image.
  • 创建一个非索引图像。
  • Create an indexed image.
  • 创建一个索引图像。

In both cases, calling the Bitmap.Dispose() method on the original Bitmap removes the lock on the file or removes the requirement that the stream or memory stay alive.

在这两种情况下,调用原始位图上的位图. dispose()方法会删除文件上的锁,或者删除流或内存保持活动的要求。

#1


6  

System.Drawing.Image.FromFile() does not close the file until you call Dispose on the image.

from文件()不关闭文件,直到您调用处理图像。

Bitmap and Image constructor dependencies:

位图和图像构造函数依赖项:

When either a Bitmap object or an Image object is constructed from a file, the file remains locked for the lifetime of the object. As a result, you cannot change an image and save it back to the same file where it originated.

当位图对象或图像对象从文件构造时,该文件将在对象的生命周期内保持锁定。因此,您无法更改映像并将其保存到它的源文件。

Additionally, if the stream was destroyed during the life of the Bitmap object, you cannot successfully access an image that was based on a stream. For example, the Graphics.DrawImage() function may not succeed after the stream has been destroyed

此外,如果流在位图对象的生命周期中被破坏,则不能成功访问基于流的映像。例如,Graphics.DrawImage()函数在流被破坏后可能不会成功

Image.FromFile() is a very poor API method (in the sense that it sets the developer up for failure!). The problem is caused by:

fromfile()是一个非常糟糕的API方法(因为它设置了开发人员失败!)问题的原因如下:

GDI+, and therefore the System.Drawing namespace, may defer the decoding of raw image bits until the bits are required by the image. Additionally, even after the image has been decoded, GDI+ may determine that it is more efficient to discard the memory for a large Bitmap and to re-decode later. Therefore, GDI+ must have access to the source bits for the image for the life of the Bitmap or the Image object.

GDI+,因此系统。绘制命名空间,可以将原始图像位的解码延迟到图像需要这些位。此外,即使在图像被解码后,GDI+可能会决定放弃一个大位图的内存并在以后重新解码,这更有效。因此,GDI+必须在位图或图像对象的生命周期中访问图像的源位。

To retain access to the source bits, GDI+ locks any source file, and forces the application to maintain the life of any source stream, for the life of the Bitmap or the Image object.

为了保持对源比特的访问,GDI+锁住任何源文件,并迫使应用程序为位图或图像对象的生命周期维护任何源流的生命周期。

Again, quoting from the support article:

再一次,引用支助条款:

To work around this problem, create new Bitmap images by using one of the following methods (as described later in this section):

要解决这个问题,可以使用以下方法之一创建新的位图图像(如本节后面所述):

  • Create a non-indexed image.
  • 创建一个非索引图像。
  • Create an indexed image.
  • 创建一个索引图像。

In both cases, calling the Bitmap.Dispose() method on the original Bitmap removes the lock on the file or removes the requirement that the stream or memory stay alive.

在这两种情况下,调用原始位图上的位图. dispose()方法会删除文件上的锁,或者删除流或内存保持活动的要求。