Image.Save (..)는 메모리 스트림이 닫혀 있기 때문에 GDI + 예외를 throw합니다.


108

이미지로 저장하려는 바이너리 데이터가 있습니다. 이미지를 저장하려고 할 때 이미지를 만드는 데 사용 된 메모리 스트림이 저장 전에 닫혔다면 예외가 발생합니다. 내가 이렇게하는 이유는 내가 동적으로 이미지를 만들고 있기 때문에 .. 메모리 스트림을 사용해야합니다.

이것은 코드입니다.

[TestMethod]
public void TestMethod1()
{
    // Grab the binary data.
    byte[] data = File.ReadAllBytes("Chick.jpg");

    // Read in the data but do not close, before using the stream.
    Stream originalBinaryDataStream = new MemoryStream(data);
    Bitmap image = new Bitmap(originalBinaryDataStream);
    image.Save(@"c:\test.jpg");
    originalBinaryDataStream.Dispose();

    // Now lets use a nice dispose, etc...
    Bitmap2 image2;
    using (Stream originalBinaryDataStream2 = new MemoryStream(data))
    {
        image2 = new Bitmap(originalBinaryDataStream2);
    }

    image2.Save(@"C:\temp\pewpew.jpg"); // This throws the GDI+ exception.
}

누구든지 스트림을 닫은 상태에서 이미지를 저장할 수있는 방법에 대한 제안이 있습니까? 이미지가 저장된 후 스트림을 닫는 것을 기억하는 개발자에게 의존 할 수 없습니다. 실제로 개발자는 이미지가 메모리 스트림을 사용하여 생성되었다는 생각이 없습니다 (다른 코드에서 발생하기 때문에).

정말 혼란 스러워요 :(


1
나는 다른 질문 에서 @HansPassant로부터이 코멘트를 받았다 . 코덱이 파일을 작성하는 데 문제가있을 때마다이 예외가 발생합니다. 추가 할 좋은 디버깅 문은 Save () 호출 전에 System.IO.File.WriteAllText (path, "test")이며 파일을 만드는 기본 기능을 확인합니다. 이제 무엇을 잘못했는지 알려주는 좋은 예외가 표시됩니다.
후안 카를로스 Oropeza

image2.Save inside usingblock. 나는 originalBinaryDataStream2 사용이 끝나면 자동으로 폐기 되었다고 생각합니다 . 그리고 그것은 예외를 던질 것입니다.
taynguyen

답변:


172

MemoryStream이므로 스트림을 닫을 필요 가 없습니다. 그렇지 않으면 나쁜 일이 발생하지 않습니다. 어쨌든 처분 할 수있는 것은 폐기하는 것이 좋습니다. ( 이 질문 보기 을 .)

그러나 Bitmap을 폐기 해야 합니다. 그러면 스트림이 닫힙니다. 기본적으로 Bitmap 생성자에 스트림을 제공하면 스트림을 "소유"하고 닫으면 안됩니다. 해당 생성자에 대한 문서는 다음과 같이 말합니다.

Bitmap의 수명 동안 스트림을 열어 두어야합니다.

비트 맵을 처리 할 때 스트림을 닫을 것을 약속하는 문서를 찾을 수 없지만 꽤 쉽게 확인할 수 있습니다.


2
대박! 그것은 훌륭한 대답 Jon입니다. 완벽한 문장을 만듭니다 (그리고 문서의 스트림에 대해 조금 놓쳤습니다). 두 엄지 손가락! 나는 그것을 시도했을 때 다시보고 할 것입니다 :)
Pure.Krome

CA2000 규칙을 준수하려면 어떻게해야하는지에 대한 의견이 있으십니까? (msdn.microsoft.com/en-us/library/ms182289.aspx)
Patrick Szalapski 2011 년

@Patrick : 단순히 적용 할 수 없습니다. 기본적으로 리소스의 소유권을 이전했습니다. 가장 가까운 방법은 Dispose 호출을 무시하는 "NonClosingStream"래퍼를 만드는 것입니다. 확실하지 ... - 내가 MiscUtil에 하나있을 수 있습니다 생각
존 소총

정보 @Jon에 감사드립니다. 나에게는 이상한 이유로 로컬 개발 환경에서 dispose ()로도 작동했지만 프로덕션에서는 작동하지 않았습니다.
Oxon

92

GDI +에서 일반 오류가 발생했습니다. 잘못된 저장 경로로 인해 발생할 수도 있습니다 ! 그것을 알아 차리는 데 반나절이 걸렸습니다. 따라서 이미지를 저장하기 위해 경로를 두 번 확인했는지 확인하십시오.


4
나는 이것을 보니 기쁘다 C\Users\mason\Desktop\pic.png. 내 길은 . 콜론이 없습니다! 나는 그것을 깨닫기 전에 영원히 보냈을 것입니다.
mason

4
부정확은 이미지를 저장할 폴더가 존재하지 않음을 의미하기도합니다.
Roemer

14

C : \ Temp 디렉토리가 존재하지 않으면 스트림이 여전히 존재하더라도이 예외가 발생한다는 점을 언급 할 가치가 있습니다.


+1이 예외는 다양한 시나리오에서 발생하는 것으로 보입니다. 잘못된 경로는 내가 오늘 만난 경로입니다.
Kirk Broadhurst 2011

4

같은 문제가 있었지만 실제로 원인은 응용 프로그램이 C에 파일을 저장할 수있는 권한이 없었기 때문입니다. "D : \ .."로 변경하면 사진이 저장되었습니다.


2

비트 맵을 복사합니다. 비트 맵의 ​​수명 동안 스트림을 열어 두어야합니다.

이미지를 그릴 때 : System.Runtime.InteropServices.ExternalException : GDI에서 일반 오류가 발생했습니다.

    public static Image ToImage(this byte[] bytes)
    {
        using (var stream = new MemoryStream(bytes))
        using (var image = Image.FromStream(stream, false, true))
        {
            return new Bitmap(image);
        }
    }

    [Test]
    public void ShouldCreateImageThatCanBeSavedWithoutOpenStream()
    {
        var imageBytes = File.ReadAllBytes("bitmap.bmp");

        var image = imageBytes.ToImage();

        image.Save("output.bmp");
    }

1
이것은 정확히 작동하지 않습니다. ToImage ()의 코드에서 로컬 "이미지"는 원본 파일이 무엇이든 (jpeg 또는 png 등)의 .RawFormat을 올바르게 갖지만 ToImage ()의 반환 값에는 예기치 않게 .RawFormat MemoryBmp가 있습니다.
Patrick Szalapski 2011 년

RawFormat그래도 그게 얼마나 중요한지 잘 모르겠습니다 . 당신이 그것을 사용하려는 경우, 길을 따라 객체 곳에서 검색,하지만 저장 당신이 실제로 원하는 유형으로, 일반적으로 할 수 있습니다 .
Nyerguds

2

비트 맵의 ​​다른 복사본을 만들 수 있습니다.

using (var memoryStream = new MemoryStream())
{
    // write to memory stream here

    memoryStream.Position = 0;
    using (var bitmap = new Bitmap(memoryStream))
    {
        var bitmap2 = new Bitmap(bitmap);
        return bitmap2;
    }
}

2

이 오류는 Citrix에서 시도 할 때 발생했습니다. 권한이없는 서버에서 이미지 폴더가 C : \로 설정되었습니다. 이미지 폴더가 공유 드라이브로 이동되면 오류가 사라졌습니다.


1

GDI +에서 일반 오류가 발생했습니다. 이미지 저장 경로 문제로 인해 발생할 수 있습니다. 저장 경로가 너무 길어서이 오류가 발생했습니다. 먼저 이미지를 최단 경로에 저장하고 긴 경로 처리 기술로 올바른 위치로 이동하여이 오류를 수정했습니다.


1

내가 실행중인 자동화 된 테스트가 존재하지 않는 폴더에 스냅 샷을 저장하려고했기 때문에이 오류가 발생했습니다. 폴더를 만든 후 오류가 해결되었습니다.


0

내 코드가 작동하도록 만든 이상한 솔루션입니다. 그림판에서 이미지를 열고 동일한 형식 (.jpg)의 새 파일로 저장합니다. 이제이 새 파일로 시도하면 작동합니다. 파일이 어떤 식 으로든 손상되었을 수 있음을 명확하게 설명합니다. 이것은 코드에 다른 모든 버그가 수정 된 경우에만 도움이 될 수 있습니다.


0

경로에 이미지를 저장하려고 할 때도 나와 함께 나타났습니다.

C:\Program Files (x86)\some_directory

그리고는 .exe관리자 권한으로 실행하는 실행되지 않았습니다,이 역시 같은 문제가있는 사람을 도움이 될 수 있기를 바랍니다.


0

나를 위해 아래 코드는 A generic error occurred in GDI+저장하는 줄에서 충돌했습니다 .MemoryStream . 코드는 웹 서버에서 실행 중이었고 사이트를 실행하는 응용 프로그램 풀을 중지하고 시작하여 문제를 해결했습니다.

GDI +의 내부 오류 였음에 틀림 없습니다.

    private static string GetThumbnailImageAsBase64String(string path)
    {
        if (path == null || !File.Exists(path))
        {
            var log = ContainerResolver.Container.GetInstance<ILog>();
            log.Info($"No file was found at path: {path}");
            return null;
        }

        var width = LibraryItemFileSettings.Instance.ThumbnailImageWidth;

        using (var image = Image.FromFile(path))
        {
            using (var thumbnail = image.GetThumbnailImage(width, width * image.Height / image.Width, null, IntPtr.Zero))
            {
                using (var memoryStream = new MemoryStream())
                {
                    thumbnail.Save(memoryStream, ImageFormat.Png); // <= crash here 
                    var bytes = new byte[memoryStream.Length];
                    memoryStream.Position = 0;
                    memoryStream.Read(bytes, 0, bytes.Length);
                    return Convert.ToBase64String(bytes, 0, bytes.Length);
                }
            }
        }
    }

0

WPF 앱에서 간단한 이미지 편집을 시도 할 때이 오류가 발생했습니다.

이미지 요소의 소스를 비트 맵으로 설정하면 파일이 저장되지 않습니다. Source = null을 설정해도 파일이 해제되지 않는 것 같습니다.

이제는 이미지를 이미지 소스 요소로 사용하지 않으므로 편집 후 덮어 쓸 수 있습니다!

편집하다

CacheOption 속성 (@Nyerguds 덕분에)에 대해들은 후 해결책을 찾았습니다. 따라서 Bitmap 생성자를 사용하는 대신을 설정 한 후 Uri를 설정해야합니다 CacheOption BitmapCacheOption.OnLoad.Image1 아래는 Wpf입니다.Image 요소)를

대신에

Image1.Source = new BitmapImage(new Uri(filepath));

사용하다:

var image = new BitmapImage();
image.BeginInit();
image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(filepath);
image.EndInit();
Image1.Source = image;

참조 : WPF 이미지 캐싱


1
WPF 이미지에는 BitmapCacheOption.OnLoad로드 소스에서 연결을 끊는 특정 매개 변수 가 있습니다.
Nyerguds

@Nyerguds 감사합니다. 귀하의 의견이 올바른 질문을하지 않을 때까지
mkb

0

이 코드를 시도하십시오.

static void Main(string[] args)
{
    byte[] data = null;
    string fullPath = @"c:\testimage.jpg";

    using (MemoryStream ms = new MemoryStream())
    using (Bitmap tmp = (Bitmap)Bitmap.FromFile(fullPath))
    using (Bitmap bm = new Bitmap(tmp))
    {
        bm.SetResolution(96, 96);
        using (EncoderParameters eps = new EncoderParameters(1))
        {   
            eps.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
            bm.Save(ms, GetEncoderInfo("image/jpeg"), eps);
        }

        data = ms.ToArray();
    }

    File.WriteAllBytes(fullPath, data);
}

private static ImageCodecInfo GetEncoderInfo(string mimeType)
{
        ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();

        for (int j = 0; j < encoders.Length; ++j)
        {
            if (String.Equals(encoders[j].MimeType, mimeType, StringComparison.InvariantCultureIgnoreCase))
                return encoders[j];
        }
    return null;
}

0

이미지 크기를 조정하기 위해 이미지 프로세서를 사용했는데 어느 날 "GDI +에서 일반 오류가 발생했습니다"예외가 발생했습니다.

잠시 살펴본 후 응용 프로그램 풀을 재활용 하고 빙고가 작동합니다. 그래서 나는 여기에 그것을 기록하고 그것이 도움이되기를 바랍니다;)

건배

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.