上传的文件被GDI读取后锁定

发布于 2024-11-29 09:25:50 字数 4735 浏览 1 评论 0原文

我有以下 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;
    }
}

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

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);
}

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

尝试重命名时锁定文件

我已确保我有 <按照许多人的建议,对 Graphics 对象进行了 code>Dispose 处理,但我不明白为什么锁不会释放?任何人都可以看到问题吗?

理想情况下,我想在底部执行此操作:

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);

并删除我用来读取的图像 - 它们不再需要。我不介意它们留在那里,只要我可以从上传者那里随意覆盖它们......

I have the following ImageObject class:

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:

locked file when trying to rename

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...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

故事和酒 2024-12-06 09:25:50

System.Drawing.Image.FromFile() 在您对图像调用 Dispose 之前,不会关闭文件。

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

当 Bitmap 对象或 Image 对象由
文件,该文件在对象的生命周期内保持锁定状态。作为一个
结果,您无法更改图像并将其保存回同一文件
它起源于哪里。

此外,如果流在生命周期内被销毁
位图对象,您无法成功访问基于位图的图像
在一条溪流上。例如,Graphics.DrawImage()函数可能不会
流被销毁后成功

Image.FromFile() 是一个非常糟糕的 API 方法(从某种意义上说,它会让开发人员陷入失败!)。问题的原因是:

GDI+,以及 System.Drawing 命名空间,可能会推迟
对原始图像位进行解码,直到图像需要这些位

此外,即使在图像被解码之后,GDI+ 也可能会被解码。
确定丢弃大量内存会更有效
位图并稍后重新解码。因此,GDI+ 必须能够访问
位图或图像生命周期内图像的源位
目的。

为了保留对源位的访问,GDI+ 锁定任何源文件,并且
强制应用程序维持任何源流​​的生命周期,例如
Bitmap 或 Image 对象的生命周期。

再次引用支持文章:

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

  • 创建非索引图像。
  • 创建索引图像。

在这两种情况下,都会在原始图像上调用 Bitmap.Dispose() 方法
位图删除了文件上的锁定或删除了以下要求:
流或内存保持活动状态。

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

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

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

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.

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.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文