JpegBitmapEncoder.Save () генерирует исключение при записи изображения с метаданными в MemoryStream - PullRequest
2 голосов
/ 24 апреля 2010

Я пытаюсь настроить метаданные на изображении JPG, чего нет. В этом случае нельзя использовать средство записи на месте (InPlaceBitmapMetadataWriter), поскольку в изображении нет места для метаданных.

Если я использую FileStream в качестве вывода - все работает нормально. Но если я пытаюсь использовать MemoryStream в качестве вывода - JpegBitmapEncoder.Save () выдает исключение (Exception from HRESULT: 0xC0000005). После некоторых исследований я также выяснил, какой кодировщик может сохранить изображение в поток памяти, если вместо метаданных я предоставляю ноль.

Я сделал очень упрощенный и короткий пример, который воспроизводит проблему:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Media.Imaging;

namespace JpegSaveTest
{
    class Program
    {
        public static JpegBitmapEncoder SetUpMetadataOnStream(Stream src, string title)
        {
            uint padding = 2048;
            BitmapDecoder original;
            BitmapFrame framecopy, newframe;
            BitmapMetadata metadata;
            JpegBitmapEncoder output = new JpegBitmapEncoder();
            src.Seek(0, SeekOrigin.Begin);
            original = JpegBitmapDecoder.Create(src, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
            if (original.Frames[0] != null) {
                framecopy = (BitmapFrame)original.Frames[0].Clone();
                if (original.Frames[0].Metadata != null) metadata = original.Frames[0].Metadata.Clone() as BitmapMetadata;
                else metadata = new BitmapMetadata("jpeg");
                metadata.SetQuery("/app1/ifd/PaddingSchema:Padding", padding);
                metadata.SetQuery("/app1/ifd/exif/PaddingSchema:Padding", padding);
                metadata.SetQuery("/xmp/PaddingSchema:Padding", padding);
                metadata.SetQuery("System.Title", title);
                newframe = BitmapFrame.Create(framecopy, framecopy.Thumbnail, metadata, original.Frames[0].ColorContexts);
                output.Frames.Add(newframe);
            }
            else {
                Exception ex = new Exception("Image contains no frames.");
                throw ex;
            }
            return output;
        }

        public static MemoryStream SetTagsInMemory(string sfname, string title)
        {
            Stream src, dst;
            JpegBitmapEncoder output;
            src = File.Open(sfname, FileMode.Open, FileAccess.Read, FileShare.Read);
            output = SetUpMetadataOnStream(src, title);
            dst = new MemoryStream();
            output.Save(dst);
            src.Close();
            return (MemoryStream)dst;
        }

        static void Main(string[] args)
        {
            string filename = "Z:\\dotnet\\gnom4.jpg";
            MemoryStream s;
            s = SetTagsInMemory(filename, "test title");
        }
    }
}

Это простое консольное приложение. Чтобы запустить его, замените содержимое переменной имени файла на путь к любому файлу .jpg без метаданных (или используйте mine ).

Ofc Я могу сначала сохранить изображение во временный файл, закрыть его, затем открыть и скопировать в MemoryStream, но это слишком грязный и медленный обходной путь. Любые идеи о том, как получить эту работу, приветствуются:)

Ответы [ 2 ]

2 голосов
/ 25 апреля 2010

Если кто-то столкнется с такой же проблемой, вот решение:

Если вы попытаетесь .Save () jpeg из основного потока приложения, добавьте [STAThread] перед Main ().

Если нет, вызовите .SetApartmentState (ApartmentState.STA) для потока, вызывающего JpegBitmapEncoder.Save ()

Версии windowscodecs.dll для WinXP и WinVista не подлежат повторному использованию, поэтому, если вы будете использовать модель MTA по умолчанию (это стандартная версия с .NET Framework 2.0) для потоков, вызывающих функцию JpegBitmapEncoder.Save (), она может вести себя странно и бросать описанные исключение. Win7 версия windowscodecs.dll не имеет этой проблемы.

1 голос
/ 24 апреля 2010

Я запустил ваш код без изменений, и он не выдал ошибку. Я даже попытался сохранить измененные данные на диск, и само изображение не было повреждено.

string filename = "e:\\a.jpg";
        MemoryStream s;
        s = SetTagsInMemory(filename, "test title");
        FileStream fs = new FileStream("e:\\b.jpg", FileMode.CreateNew, FileAccess.ReadWrite);
        BinaryWriter sw = new BinaryWriter(fs);
        s.Seek(0, SeekOrigin.Begin);
        while (s.Position < s.Length)
       {
            byte[] data = new byte[4096];
            s.Read(data, 0, data.Length);
            sw.Write(data);
       }

        sw.Flush();
        sw.Close();
        fs.Close();

Кроме того, что я добавил ниже s = SetTagsInMemory (...) для записи на диск, остальная часть вашего кода не изменена.

Редактировать: о, и метаданные определенно оказались в новом файле, предыдущий не имел метаданных из того, что я мог видеть.

...