Image.Save (..) ném ngoại lệ GDI + vì luồng bộ nhớ bị đóng


108

tôi có một số dữ liệu nhị phân mà tôi muốn lưu dưới dạng hình ảnh. Khi tôi cố gắng lưu hình ảnh, nó ném ra một ngoại lệ nếu luồng bộ nhớ được sử dụng để tạo hình ảnh, đã bị đóng trước khi lưu. Lý do tôi làm điều này là vì tôi đang tạo hình ảnh động và như vậy .. tôi cần sử dụng luồng bộ nhớ.

đây là mã:

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

Có ai có bất kỳ đề xuất nào về cách tôi có thể lưu hình ảnh khi luồng bị đóng không? Tôi không thể dựa vào các nhà phát triển để nhớ đóng luồng sau khi hình ảnh được lưu. Trên thực tế, nhà phát triển sẽ KHÔNG CÓ Ý TƯỞNG rằng hình ảnh được tạo bằng luồng bộ nhớ (vì nó xảy ra trong một số mã khác, ở nơi khác).

Tôi thực sự bối rối :(


1
Tôi đã nhận được nhận xét này từ @HansPassant trong một câu hỏi khác . Bạn sẽ nhận được ngoại lệ này bất cứ khi nào codec gặp sự cố khi ghi tệp. Một câu lệnh gỡ lỗi tốt để thêm là System.IO.File.WriteAllText (đường dẫn, "kiểm tra") trước lệnh gọi Save (), nó xác minh khả năng cơ bản để tạo tệp. Bây giờ bạn sẽ nhận được một ngoại lệ tốt cho bạn biết bạn đã làm gì sai.
Juan Carlos Oropeza,

Bạn nên image2.Save bên trong usingkhối. Tôi nghĩ rằng nó originalBinaryDataStream2 đã được tự động xử lý khi kết thúc việc sử dụng. Và điều đó sẽ ném ra ngoại lệ.
taynguyen

Câu trả lời:


172

Vì là MemoryStream, bạn thực sự không cần phải đóng luồng - sẽ không có gì xấu nếu bạn không làm vậy, mặc dù rõ ràng là bạn nên vứt bỏ bất cứ thứ gì dùng một lần. (Xem câu hỏi này để biết thêm về điều này.)

Tuy nhiên, bạn nên loại bỏ Bitmap - và điều đó sẽ đóng luồng cho bạn. Về cơ bản khi bạn cung cấp cho phương thức khởi tạo Bitmap một luồng, nó sẽ "sở hữu" luồng đó và bạn không nên đóng nó. Như các tài liệu cho hàm tạo đó nói:

Bạn phải mở luồng trong suốt thời gian tồn tại của Bitmap.

Tôi không thể tìm thấy bất kỳ tài liệu nào hứa hẹn đóng luồng khi bạn hủy bitmap, nhưng bạn có thể xác minh điều đó khá dễ dàng.


2
tuyệt vời! đó là một câu trả lời tuyệt vời Jon. Tạo nên sự hoàn hảo (và tôi đã bỏ lỡ một chút về luồng trong tài liệu). Hai ngón tay cái lên! Tôi sẽ báo cáo lại khi tôi đã thử :)
Pure.Krome

Bất kỳ nhận xét nào về cách thực hiện điều này nếu chúng ta muốn tuân theo quy tắc CA2000? (msdn.microsoft.com/en-us/library/ms182289.aspx)
Patrick Szalapski

@Patrick: Nó chỉ đơn giản là không áp dụng - về cơ bản bạn đã chuyển quyền sở hữu tài nguyên. Cách gần nhất bạn có thể làm là tạo một trình bao bọc "NonClosingStream" bỏ qua lệnh gọi Dispose. Tôi nghĩ rằng tôi có thể có một trong MiscUtil - không chắc chắn ...
Jon Skeet

Cảm ơn vì thông tin @Jon. Đối với tôi, vì một số lý do kỳ lạ, nó hoạt động ngay cả với dispose () trong môi trường local dev nhưng không hoạt động trong sản xuất.
Oxon

92

Đã xảy ra lỗi chung trong GDI +. Cũng có thể do đường dẫn lưu không chính xác ! Tôi đã mất nửa ngày để nhận thấy điều đó. Vì vậy, hãy đảm bảo rằng bạn cũng đã kiểm tra kỹ đường dẫn để lưu ảnh.


4
Tôi rất vui vì tôi đã thấy điều này, con đường của tôi đã C\Users\mason\Desktop\pic.png. Thiếu dấu hai chấm! Tôi sẽ dành mãi mãi trước khi tôi nhận ra điều đó.
thợ nề

4
Không chính xác cũng có nghĩa là thư mục bạn muốn lưu hình ảnh không tồn tại.
Roemer

14

Có lẽ điều đáng nói là nếu thư mục C: \ Temp không tồn tại, nó cũng sẽ ném ra ngoại lệ này ngay cả khi luồng của bạn vẫn tồn tại.


+1 Ngoại lệ này dường như xảy ra trong nhiều trường hợp. Đường dẫn không hợp lệ là đường dẫn tôi gặp phải hôm nay.
Kirk Broadhurst

4

Tôi đã gặp vấn đề tương tự nhưng thực sự nguyên nhân là do ứng dụng không có quyền lưu tệp trên C. Khi tôi thay đổi thành "D: \ ..", ảnh đã được lưu.


2

Sao chép Bitmap. Bạn phải giữ luồng mở trong suốt thời gian tồn tại của bitmap.

Khi vẽ một hình ảnh: System.Runtime.InteropServices.ExternalException: Một lỗi chung đã xảy ra trong 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
Điều này không hoạt động chính xác; trong mã của bạn trong ToImage (), "hình ảnh" cục bộ chính xác sẽ có .RawFormat của bất kỳ tệp gốc nào là (jpeg hoặc png, v.v.), trong khi giá trị trả về của ToImage () sẽ bất ngờ có .RawFormat MemoryBmp.
Patrick Szalapski

Tuy nhiên, không chắc chắn RawFormatvấn đề nhiều như thế nào . Nếu bạn muốn sử dụng, hãy lấy nó từ đối tượng ở đâu đó trên đường đi, nhưng nói chung, hãy lưu dưới dạng bất kỳ loại nào bạn thực sự muốn .
Nyerguds

2

Bạn có thể thử tạo một bản sao bitmap khác:

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

Lỗi này xảy ra với tôi khi tôi đang cố gắng từ Citrix. Thư mục hình ảnh đã được đặt thành C: \ trong máy chủ, mà tôi không có đặc quyền. Khi thư mục hình ảnh được chuyển sang bộ nhớ dùng chung, lỗi đã biến mất.


1

Đã xảy ra lỗi chung trong GDI +. Nó có thể xảy ra do các vấn đề về đường dẫn lưu trữ hình ảnh, tôi gặp lỗi này do đường dẫn lưu trữ của tôi quá dài, tôi đã sửa lỗi này bằng cách lưu trữ hình ảnh theo đường ngắn nhất và di chuyển nó đến đúng vị trí bằng các kỹ thuật xử lý đường dẫn dài.


1

Tôi gặp phải lỗi này, vì quá trình kiểm tra tự động mà tôi đang thực hiện, đang cố gắng lưu trữ ảnh chụp nhanh vào một thư mục không tồn tại. Sau khi tôi tạo thư mục, lỗi đã được giải quyết


0

Một giải pháp kỳ lạ đã làm cho mã của tôi hoạt động. Mở hình ảnh trong paint và lưu nó thành một tệp mới có cùng định dạng (.jpg). Bây giờ hãy thử với tệp mới này và nó hoạt động. Nó giải thích rõ ràng cho bạn rằng tệp có thể bị hỏng theo một cách nào đó. Điều này chỉ có thể hữu ích nếu mã của bạn đã được sửa mọi lỗi khác


0

Nó cũng đã xuất hiện với tôi khi tôi cố gắng lưu một hình ảnh vào đường dẫn

C:\Program Files (x86)\some_directory

.exekhông được thực thi để chạy với tư cách quản trị viên, tôi hy vọng điều này cũng có thể giúp ích cho những người gặp vấn đề tương tự.


0

Đối với tôi, mã dưới đây bị lỗi A generic error occurred in GDI+trên dòng Lưu vào a MemoryStream. Mã đang chạy trên máy chủ web và tôi đã giải quyết nó bằng cách dừng và khởi động Nhóm ứng dụng đang chạy trang web.

Chắc là một số lỗi nội bộ trong 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

Tôi đã gặp lỗi này khi tôi đang cố gắng chỉnh sửa hình ảnh đơn giản trong ứng dụng WPF.

Đặt Nguồn của phần tử Hình ảnh thành bitmap ngăn chặn việc lưu tệp. Ngay cả thiết lập Source = null dường như không giải phóng tệp.

Bây giờ tôi không bao giờ sử dụng hình ảnh làm phần tử Nguồn của Hình ảnh, vì vậy tôi có thể ghi đè sau khi chỉnh sửa!

BIÊN TẬP

Sau khi nghe về thuộc tính CacheOption (Cảm ơn @Nyerguds), tôi đã tìm ra giải pháp: Vì vậy, thay vì sử dụng hàm tạo Bitmap, tôi phải đặt Uri sau khi thiết lập CacheOption BitmapCacheOption.OnLoad. (Bên Image1dưới là Imagephần tử Wpf )

Thay vì

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

Sử dụng:

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

Xem cái này: WPF Image Caching


1
Hình ảnh WPF có một tham số cụ thể BitmapCacheOption.OnLoadđể ngắt kết nối chúng khỏi nguồn tải.
Nyerguds

Cảm ơn bạn @Nyerguds, cho đến khi nhận xét của bạn, tôi không đặt được câu hỏi đúng
mkb

0

Hãy thử mã này:

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

Tôi đã sử dụng bộ xử lý hình ảnh để thay đổi kích thước hình ảnh và một ngày tôi nhận được ngoại lệ "Đã xảy ra lỗi chung trong GDI +".

Sau khi tra cứu một lúc, tôi đã cố gắng tái chế nhóm ứng dụng và chơi lô tô nó hoạt động. Vì vậy, tôi lưu ý nó ở đây, hy vọng nó sẽ giúp ích;)

Chúc mừng

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.